GCW Zero
更換螢幕(3.5吋 IPS HX8347-A01 解析度320x240)
由於GCW0掌機使用的螢幕並非IPS規格,因此,司徒致力找尋可以替換的屏幕,最後在淘寶網找到一片3.5吋IPS屏,解析度也跟原本GCW0一樣,替換過程如下說明。
對應腳位:
GCW0 | HX8347-A01 |
---|---|
LED-K | LED-K(K1~K8) |
LED-A | LED-A |
RESET | NRESET |
CS | NCS |
SCK | DNC_SCL |
SDI | SDI |
B2 | B0 |
B3 | B1 |
B4 | B2 |
B5 | B3 |
B6 | B4 |
B7 | B5 |
G2 | G0 |
G3 | G1 |
G4 | G2 |
G5 | G3 |
G6 | G4 |
G7 | G5 |
R2 | R0 |
R3 | R1 |
R4 | R2 |
R5 | R3 |
R6 | R4 |
R7 | R5 |
HSYNC | HSYNC |
VSYNC | VSYNC |
DCLK | PCLK |
VDD | IOVCC, VCI, BS0. BS1, BS2, NRD, NWR |
ENB | DE |
GND | GND |
drivers/video/displays/Kconfig
config PANEL_HX8347A01 # TODO: Switch to tristate once the driver is a module. bool "HX8347A01 based panel" help Select this if you are using a HX8347A01 LCD driver IC.
drivers/video/displays/Makefile
obj-$(CONFIG_PANEL_HX8347A01) += panel-hx8347a01.o
drivers/video/displays/panel-hx8347a01.c
#include <linux/delay.h> #include <linux/device.h> #include <linux/gfp.h> #include <linux/gpio.h> #include <video/jzpanel.h> #include <video/panel-hx8347a01.h> struct hx8347a01 { struct hx8347a01_platform_data *pdata; }; static void hx8347a01_send_spi(struct hx8347a01_platform_data *pdata, u8 data) { int bit; for(bit=7; bit>=0; bit--){ gpio_direction_output(pdata->gpio_clock, 0); gpio_direction_output(pdata->gpio_data, (data >> bit) & 1); udelay(10); gpio_direction_output(pdata->gpio_clock, 1); udelay(10); } } static void hx8347a01_send_cmd(struct hx8347a01_platform_data *pdata, u8 data) { gpio_direction_output(pdata->gpio_enable, 0); hx8347a01_send_spi(pdata, 0x74); hx8347a01_send_spi(pdata, data >> 8); hx8347a01_send_spi(pdata, data); gpio_direction_output(pdata->gpio_enable, 1); } static void hx8347a01_send_data(struct hx8347a01_platform_data *pdata, u8 data) { gpio_direction_output(pdata->gpio_enable, 0); hx8347a01_send_spi(pdata, 0x76); hx8347a01_send_spi(pdata, data >> 8); hx8347a01_send_spi(pdata, data); gpio_direction_output(pdata->gpio_enable, 1); } static int hx8347a01_panel_init(void **out_panel, struct device *dev, void *panel_pdata) { struct hx8347a01_platform_data *pdata = panel_pdata; struct hx8347a01 *panel; int ret; printk("%s++\n", __func__); panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); if (!panel) { dev_err(dev, "Failed to alloc panel data\n"); return -ENOMEM; } panel->pdata = pdata; *out_panel = panel; /* Reserve GPIO pins. */ ret = devm_gpio_request(dev, pdata->gpio_reset, "LCD panel reset"); if (ret) { dev_err(dev, "Failed to request LCD panel reset pin: %d\n", ret); return ret; } ret = devm_gpio_request(dev, pdata->gpio_clock, "LCD 3-wire clock"); if (ret) { dev_err(dev, "Failed to request LCD panel 3-wire clock pin: %d\n", ret); return ret; } ret = devm_gpio_request(dev, pdata->gpio_enable, "LCD 3-wire enable"); if (ret) { dev_err(dev, "Failed to request LCD panel 3-wire enable pin: %d\n", ret); return ret; } ret = devm_gpio_request(dev, pdata->gpio_data, "LCD 3-wire data"); if (ret) { dev_err(dev, "Failed to request LCD panel 3-wire data pin: %d\n", ret); return ret; } /* Set initial GPIO pin directions and value. */ gpio_direction_output(pdata->gpio_clock, 1); gpio_direction_output(pdata->gpio_enable, 1); gpio_direction_output(pdata->gpio_data, 0); printk("%s--\n", __func__); return 0; } static void hx8347a01_panel_exit(void *panel) { } static void hx8347a01_panel_enable(void *panel) { struct hx8347a01_platform_data *pdata = ((struct hx8347a01 *)panel)->pdata; printk("%s++\n", __func__); // Reset LCD panel gpio_direction_output(pdata->gpio_reset, 0); mdelay(50); gpio_direction_output(pdata->gpio_reset, 1); mdelay(50); // GAMMA SETTING hx8347a01_send_cmd(pdata, 0x0046);hx8347a01_send_data(pdata, 0x0044); hx8347a01_send_cmd(pdata, 0x0047);hx8347a01_send_data(pdata, 0x0044); hx8347a01_send_cmd(pdata, 0x0048);hx8347a01_send_data(pdata, 0x0045); hx8347a01_send_cmd(pdata, 0x0049);hx8347a01_send_data(pdata, 0x0025); hx8347a01_send_cmd(pdata, 0x004A);hx8347a01_send_data(pdata, 0x0023); hx8347a01_send_cmd(pdata, 0x004B);hx8347a01_send_data(pdata, 0x0045); hx8347a01_send_cmd(pdata, 0x004C);hx8347a01_send_data(pdata, 0x0025); hx8347a01_send_cmd(pdata, 0x004D);hx8347a01_send_data(pdata, 0x0023); hx8347a01_send_cmd(pdata, 0x004E);hx8347a01_send_data(pdata, 0x0050); hx8347a01_send_cmd(pdata, 0x004F);hx8347a01_send_data(pdata, 0x0000); hx8347a01_send_cmd(pdata, 0x0050);hx8347a01_send_data(pdata, 0x0050); hx8347a01_send_cmd(pdata, 0x0051);hx8347a01_send_data(pdata, 0x0000); // 240x320 window setting hx8347a01_send_cmd(pdata, 0x0002);hx8347a01_send_data(pdata, 0x0000); // Column address start2 hx8347a01_send_cmd(pdata, 0x0003);hx8347a01_send_data(pdata, 0x0000); // Column address start1 hx8347a01_send_cmd(pdata, 0x0004);hx8347a01_send_data(pdata, 0x0000); // Column address end2 hx8347a01_send_cmd(pdata, 0x0005);hx8347a01_send_data(pdata, 0x00EF); // Column address end1 hx8347a01_send_cmd(pdata, 0x0006);hx8347a01_send_data(pdata, 0x0000); // Row address start2 hx8347a01_send_cmd(pdata, 0x0007);hx8347a01_send_data(pdata, 0x0000); // Row address start1 hx8347a01_send_cmd(pdata, 0x0008);hx8347a01_send_data(pdata, 0x0001); // Row address end2 hx8347a01_send_cmd(pdata, 0x0009);hx8347a01_send_data(pdata, 0x003F); // Row address end1 // Display Setting hx8347a01_send_cmd(pdata, 0x0016);hx8347a01_send_data(pdata, 0x0008); hx8347a01_send_cmd(pdata, 0x0038);hx8347a01_send_data(pdata, 0x0000); // RGB_EN=0 hx8347a01_send_cmd(pdata, 0x0023);hx8347a01_send_data(pdata, 0x0095); // N_DC=1001 0101 hx8347a01_send_cmd(pdata, 0x0024);hx8347a01_send_data(pdata, 0x0095); // PI_DC=1001 0101 hx8347a01_send_cmd(pdata, 0x0025);hx8347a01_send_data(pdata, 0x00FF); // I_DC=1111 1111 hx8347a01_send_cmd(pdata, 0x0027);hx8347a01_send_data(pdata, 0x0002); // N_BP=0000 0010 hx8347a01_send_cmd(pdata, 0x0028);hx8347a01_send_data(pdata, 0x0002); // N_FP=0000 0010 hx8347a01_send_cmd(pdata, 0x0029);hx8347a01_send_data(pdata, 0x0002); // PI_BP=0000 0010 hx8347a01_send_cmd(pdata, 0x002A);hx8347a01_send_data(pdata, 0x0002); // PI_FP=0000 0010 hx8347a01_send_cmd(pdata, 0x002C);hx8347a01_send_data(pdata, 0x0002); // I_BP=0000 0010 hx8347a01_send_cmd(pdata, 0x002D);hx8347a01_send_data(pdata, 0x0002); // I_FP=0000 0010 hx8347a01_send_cmd(pdata, 0x003A);hx8347a01_send_data(pdata, 0x00A1); // N_RTN=0000, N_NW=001 hx8347a01_send_cmd(pdata, 0x003B);hx8347a01_send_data(pdata, 0x00A1); // PI_RTN=0000, PI_NW=000 hx8347a01_send_cmd(pdata, 0x003C);hx8347a01_send_data(pdata, 0x00A0); // I_RTN=1111, I_NW=000 hx8347a01_send_cmd(pdata, 0x003D);hx8347a01_send_data(pdata, 0x0000); // DIV=00 mdelay(20); // Power Supply Setting hx8347a01_send_cmd(pdata, 0x0019);hx8347a01_send_data(pdata, 0x0049); // CADJ=0100, CUADJ=100(FR:60Hz, OSD_EN=1 hx8347a01_send_cmd(pdata, 0x0093);hx8347a01_send_data(pdata, 0x000F); // RADJ=1111, 100% mdelay(10); hx8347a01_send_cmd(pdata, 0x0020);hx8347a01_send_data(pdata, 0x0010); // BT=0100 hx8347a01_send_cmd(pdata, 0x001D);hx8347a01_send_data(pdata, 0x0001); // VC1=111 hx8347a01_send_cmd(pdata, 0x001E);hx8347a01_send_data(pdata, 0x0006); // VC3=000 hx8347a01_send_cmd(pdata, 0x001F);hx8347a01_send_data(pdata, 0x000C); // VRH=0100 // VCOM Setting for CMO 3.2 Panel hx8347a01_send_cmd(pdata, 0x0044);hx8347a01_send_data(pdata, 0x004C); // VCM=100 1101 hx8347a01_send_cmd(pdata, 0x0045);hx8347a01_send_data(pdata, 0x0010); // VDV=1 0001 hx8347a01_send_cmd(pdata, 0x001C);hx8347a01_send_data(pdata, 0x0004); // AP=100 mdelay(20); hx8347a01_send_cmd(pdata, 0x001B);hx8347a01_send_data(pdata, 0x0008); // GASENB=0, PON=1, DK=1, XDK=0, VLCD_TRI=0, STB=0 mdelay(40); hx8347a01_send_cmd(pdata, 0x001B);hx8347a01_send_data(pdata, 0x0014); // GASENB=0, PON=1, DK=0, XDK=0, VLCD_TRI=0, STB=0 mdelay(40); hx8347a01_send_cmd(pdata, 0x0043);hx8347a01_send_data(pdata, 0x0080); // Set VCOMG=1 mdelay(100); // Display ON Setting hx8347a01_send_cmd(pdata, 0x0090);hx8347a01_send_data(pdata, 0x00F0); // SAP=0111 1111 hx8347a01_send_cmd(pdata, 0x0026);hx8347a01_send_data(pdata, 0x0024); // GON=0, DTE=0, D=01 mdelay(40); hx8347a01_send_cmd(pdata, 0x0026);hx8347a01_send_data(pdata, 0x0024); // GON=1, DTE=0, D=01 hx8347a01_send_cmd(pdata, 0x0026);hx8347a01_send_data(pdata, 0x002C); // GON=1, DTE=0, D=11 mdelay(40); hx8347a01_send_cmd(pdata, 0x0026);hx8347a01_send_data(pdata, 0x003C); // GON=1, DTE=1, D=11 // Cycle Control hx8347a01_send_cmd(pdata, 0x0035);hx8347a01_send_data(pdata, 0x0038); // EQS=38h hx8347a01_send_cmd(pdata, 0x0036);hx8347a01_send_data(pdata, 0x0078); // EQP=78h hx8347a01_send_cmd(pdata, 0x003E);hx8347a01_send_data(pdata, 0x0038); // SON=38h hx8347a01_send_cmd(pdata, 0x0040);hx8347a01_send_data(pdata, 0x000F); // GDON=0Fh // Display Control hx8347a01_send_cmd(pdata, 0x0041);hx8347a01_send_data(pdata, 0x00F0); // GDOFF // Internal register setting hx8347a01_send_cmd(pdata, 0x0095);hx8347a01_send_data(pdata, 0x0001); // Set Display clock and Pumping clock to synchronize // RGB Interface Control,RGB polarity(HS,VS,DE,DOTCLK) hx8347a01_send_cmd(pdata, 0x0038);hx8347a01_send_data(pdata, 0x0010); // RGB Interface ON // Internal Use hx8347a01_send_cmd(pdata, 0x0070);hx8347a01_send_data(pdata, 0x0026); // Internal Use GS SS printk("%s--\n", __func__); } static void hx8347a01_panel_disable(void *panel) { struct hx8347a01_platform_data *pdata = ((struct hx8347a01 *)panel)->pdata; printk("%s++\n", __func__); hx8347a01_send_cmd(pdata, 0x0026);hx8347a01_send_data(pdata, 0x0000); printk("%s--\n", __func__); } struct panel_ops hx8347a01_panel_ops = { .init = hx8347a01_panel_init, .exit = hx8347a01_panel_exit, .enable = hx8347a01_panel_enable, .disable = hx8347a01_panel_disable, };
drivers/video/jz4770_fb.c
/* * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> * JZ4740 SoC LCD framebuffer driver * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/console.h> #include <linux/fb.h> #include <linux/dma-mapping.h> #include <asm/mach-jz4740/jz4740_fb.h> #include <asm/mach-jz4740/gpio.h> #define JZ_REG_LCD_CFG 0x00 #define JZ_REG_LCD_VSYNC 0x04 #define JZ_REG_LCD_HSYNC 0x08 #define JZ_REG_LCD_VAT 0x0C #define JZ_REG_LCD_DAH 0x10 #define JZ_REG_LCD_DAV 0x14 #define JZ_REG_LCD_PS 0x18 #define JZ_REG_LCD_CLS 0x1C #define JZ_REG_LCD_SPL 0x20 #define JZ_REG_LCD_REV 0x24 #define JZ_REG_LCD_CTRL 0x30 #define JZ_REG_LCD_STATE 0x34 #define JZ_REG_LCD_IID 0x38 #define JZ_REG_LCD_DA0 0x40 #define JZ_REG_LCD_SA0 0x44 #define JZ_REG_LCD_FID0 0x48 #define JZ_REG_LCD_CMD0 0x4C #define JZ_REG_LCD_DA1 0x50 #define JZ_REG_LCD_SA1 0x54 #define JZ_REG_LCD_FID1 0x58 #define JZ_REG_LCD_CMD1 0x5C #define JZ_LCD_CFG_SLCD BIT(31) #define JZ_LCD_CFG_PS_DISABLE BIT(23) #define JZ_LCD_CFG_CLS_DISABLE BIT(22) #define JZ_LCD_CFG_SPL_DISABLE BIT(21) #define JZ_LCD_CFG_REV_DISABLE BIT(20) #define JZ_LCD_CFG_HSYNCM BIT(19) #define JZ_LCD_CFG_PCLKM BIT(18) #define JZ_LCD_CFG_INV BIT(17) #define JZ_LCD_CFG_SYNC_DIR BIT(16) #define JZ_LCD_CFG_PS_POLARITY BIT(15) #define JZ_LCD_CFG_CLS_POLARITY BIT(14) #define JZ_LCD_CFG_SPL_POLARITY BIT(13) #define JZ_LCD_CFG_REV_POLARITY BIT(12) #define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11) #define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10) #define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9) #define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8) #define JZ_LCD_CFG_18_BIT BIT(7) #define JZ_LCD_CFG_PDW (BIT(5) | BIT(4)) #define JZ_LCD_CFG_MODE_MASK 0xf #define JZ_LCD_CTRL_BURST_4 (0x0 << 28) #define JZ_LCD_CTRL_BURST_8 (0x1 << 28) #define JZ_LCD_CTRL_BURST_16 (0x2 << 28) #define JZ_LCD_CTRL_RGB555 BIT(27) #define JZ_LCD_CTRL_OFUP BIT(26) #define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24) #define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24) #define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24) #define JZ_LCD_CTRL_PDD_MASK (0xff << 16) #define JZ_LCD_CTRL_EOF_IRQ BIT(13) #define JZ_LCD_CTRL_SOF_IRQ BIT(12) #define JZ_LCD_CTRL_OFU_IRQ BIT(11) #define JZ_LCD_CTRL_IFU0_IRQ BIT(10) #define JZ_LCD_CTRL_IFU1_IRQ BIT(9) #define JZ_LCD_CTRL_DD_IRQ BIT(8) #define JZ_LCD_CTRL_QDD_IRQ BIT(7) #define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6) #define JZ_LCD_CTRL_LSB_FISRT BIT(5) #define JZ_LCD_CTRL_DISABLE BIT(4) #define JZ_LCD_CTRL_ENABLE BIT(3) #define JZ_LCD_CTRL_BPP_1 0x0 #define JZ_LCD_CTRL_BPP_2 0x1 #define JZ_LCD_CTRL_BPP_4 0x2 #define JZ_LCD_CTRL_BPP_8 0x3 #define JZ_LCD_CTRL_BPP_15_16 0x4 #define JZ_LCD_CTRL_BPP_18_24 0x5 #define JZ_LCD_CMD_SOF_IRQ BIT(15) #define JZ_LCD_CMD_EOF_IRQ BIT(16) #define JZ_LCD_CMD_ENABLE_PAL BIT(12) #define JZ_LCD_SYNC_MASK 0x3ff #define JZ_LCD_STATE_DISABLED BIT(0) struct jzfb_framedesc { uint32_t next; uint32_t addr; uint32_t id; uint32_t cmd; } __packed; struct jzfb { struct fb_info *fb; struct platform_device *pdev; void __iomem *base; struct resource *mem; struct jz4740_fb_platform_data *pdata; size_t vidmem_size; void *vidmem; dma_addr_t vidmem_phys; struct jzfb_framedesc *framedesc; dma_addr_t framedesc_phys; struct clk *ldclk; struct clk *lpclk; unsigned is_enabled:1; struct mutex lock; uint32_t pseudo_palette[16]; }; static const struct fb_fix_screeninfo jzfb_fix = { .id = "JZ4740 FB", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .xpanstep = 0, .ypanstep = 0, .ywrapstep = 0, .accel = FB_ACCEL_NONE, }; static const struct jz_gpio_bulk_request jz_lcd_ctrl_pins[] = { JZ_GPIO_BULK_PIN(LCD_PCLK), JZ_GPIO_BULK_PIN(LCD_HSYNC), JZ_GPIO_BULK_PIN(LCD_VSYNC), JZ_GPIO_BULK_PIN(LCD_DE), JZ_GPIO_BULK_PIN(LCD_PS), JZ_GPIO_BULK_PIN(LCD_REV), JZ_GPIO_BULK_PIN(LCD_CLS), JZ_GPIO_BULK_PIN(LCD_SPL), }; static const struct jz_gpio_bulk_request jz_lcd_data_pins[] = { JZ_GPIO_BULK_PIN(LCD_DATA0), JZ_GPIO_BULK_PIN(LCD_DATA1), JZ_GPIO_BULK_PIN(LCD_DATA2), JZ_GPIO_BULK_PIN(LCD_DATA3), JZ_GPIO_BULK_PIN(LCD_DATA4), JZ_GPIO_BULK_PIN(LCD_DATA5), JZ_GPIO_BULK_PIN(LCD_DATA6), JZ_GPIO_BULK_PIN(LCD_DATA7), JZ_GPIO_BULK_PIN(LCD_DATA8), JZ_GPIO_BULK_PIN(LCD_DATA9), JZ_GPIO_BULK_PIN(LCD_DATA10), JZ_GPIO_BULK_PIN(LCD_DATA11), JZ_GPIO_BULK_PIN(LCD_DATA12), JZ_GPIO_BULK_PIN(LCD_DATA13), JZ_GPIO_BULK_PIN(LCD_DATA14), JZ_GPIO_BULK_PIN(LCD_DATA15), JZ_GPIO_BULK_PIN(LCD_DATA16), JZ_GPIO_BULK_PIN(LCD_DATA17), }; static unsigned int jzfb_num_ctrl_pins(struct jzfb *jzfb) { unsigned int num; switch (jzfb->pdata->lcd_type) { case JZ_LCD_TYPE_GENERIC_16_BIT: num = 4; break; case JZ_LCD_TYPE_GENERIC_18_BIT: num = 4; break; case JZ_LCD_TYPE_8BIT_SERIAL: num = 3; break; case JZ_LCD_TYPE_SPECIAL_TFT_1: case JZ_LCD_TYPE_SPECIAL_TFT_2: case JZ_LCD_TYPE_SPECIAL_TFT_3: num = 8; break; default: num = 0; break; } return num; } static unsigned int jzfb_num_data_pins(struct jzfb *jzfb) { unsigned int num; switch (jzfb->pdata->lcd_type) { case JZ_LCD_TYPE_GENERIC_16_BIT: num = 16; break; case JZ_LCD_TYPE_GENERIC_18_BIT: num = 18; break; case JZ_LCD_TYPE_8BIT_SERIAL: num = 8; break; case JZ_LCD_TYPE_SPECIAL_TFT_1: case JZ_LCD_TYPE_SPECIAL_TFT_2: case JZ_LCD_TYPE_SPECIAL_TFT_3: if (jzfb->pdata->bpp == 18) num = 18; else num = 16; break; default: num = 0; break; } return num; } /* Based on CNVT_TOHW macro from skeletonfb.c */ static inline uint32_t jzfb_convert_color_to_hw(unsigned val, struct fb_bitfield *bf) { return (((val << bf->length) + 0x7FFF - val) >> 16) << bf->offset; } static int jzfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *fb) { uint32_t color; if (regno >= 16) return -EINVAL; color = jzfb_convert_color_to_hw(red, &fb->var.red); color |= jzfb_convert_color_to_hw(green, &fb->var.green); color |= jzfb_convert_color_to_hw(blue, &fb->var.blue); color |= jzfb_convert_color_to_hw(transp, &fb->var.transp); ((uint32_t *)(fb->pseudo_palette))[regno] = color; return 0; } static int jzfb_get_controller_bpp(struct jzfb *jzfb) { switch (jzfb->pdata->bpp) { case 18: case 24: return 32; case 15: return 16; default: return jzfb->pdata->bpp; } } static struct fb_videomode *jzfb_get_mode(struct jzfb *jzfb, struct fb_var_screeninfo *var) { size_t i; struct fb_videomode *mode = jzfb->pdata->modes; for (i = 0; i < jzfb->pdata->num_modes; ++i, ++mode) { if (mode->xres == var->xres && mode->yres == var->yres) return mode; } return NULL; } static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb) { struct jzfb *jzfb = fb->par; struct fb_videomode *mode; if (var->bits_per_pixel != jzfb_get_controller_bpp(jzfb) && var->bits_per_pixel != jzfb->pdata->bpp) return -EINVAL; mode = jzfb_get_mode(jzfb, var); if (mode == NULL) return -EINVAL; fb_videomode_to_var(var, mode); switch (jzfb->pdata->bpp) { case 8: break; case 15: var->red.offset = 10; var->red.length = 5; var->green.offset = 6; var->green.length = 5; var->blue.offset = 0; var->blue.length = 5; break; case 16: var->red.offset = 11; var->red.length = 5; var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; break; case 18: var->red.offset = 16; var->red.length = 6; var->green.offset = 8; var->green.length = 6; var->blue.offset = 0; var->blue.length = 6; var->bits_per_pixel = 32; break; case 32: case 24: var->transp.offset = 24; var->transp.length = 8; var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->bits_per_pixel = 32; break; default: break; } return 0; } static int jzfb_set_par(struct fb_info *info) { struct jzfb *jzfb = info->par; struct jz4740_fb_platform_data *pdata = jzfb->pdata; struct fb_var_screeninfo *var = &info->var; struct fb_videomode *mode; uint16_t hds, vds; uint16_t hde, vde; uint16_t ht, vt; uint32_t ctrl; uint32_t cfg; unsigned long rate; mode = jzfb_get_mode(jzfb, var); if (mode == NULL) return -EINVAL; if (mode == info->mode) return 0; info->mode = mode; hds = mode->hsync_len + mode->left_margin; hde = hds + mode->xres; ht = hde + mode->right_margin; vds = mode->vsync_len + mode->upper_margin; vde = vds + mode->yres; vt = vde + mode->lower_margin; ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16; switch (pdata->bpp) { case 1: ctrl |= JZ_LCD_CTRL_BPP_1; break; case 2: ctrl |= JZ_LCD_CTRL_BPP_2; break; case 4: ctrl |= JZ_LCD_CTRL_BPP_4; break; case 8: ctrl |= JZ_LCD_CTRL_BPP_8; break; case 15: ctrl |= JZ_LCD_CTRL_RGB555; /* Falltrough */ case 16: ctrl |= JZ_LCD_CTRL_BPP_15_16; break; case 18: case 24: case 32: ctrl |= JZ_LCD_CTRL_BPP_18_24; break; default: break; } cfg = pdata->lcd_type & 0xf; if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT)) cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW; if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT)) cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW; if (pdata->pixclk_falling_edge) cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE; if (pdata->date_enable_active_low) cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW; if (pdata->lcd_type == JZ_LCD_TYPE_GENERIC_18_BIT) cfg |= JZ_LCD_CFG_18_BIT; if (mode->pixclock) { rate = PICOS2KHZ(mode->pixclock) * 1000; mode->refresh = rate / vt / ht; } else { if (pdata->lcd_type == JZ_LCD_TYPE_8BIT_SERIAL) rate = mode->refresh * (vt + 2 * mode->xres) * ht; else rate = mode->refresh * vt * ht; mode->pixclock = KHZ2PICOS(rate / 1000); } mutex_lock(&jzfb->lock); if (!jzfb->is_enabled) clk_enable(jzfb->ldclk); else ctrl |= JZ_LCD_CTRL_ENABLE; switch (pdata->lcd_type) { case JZ_LCD_TYPE_SPECIAL_TFT_1: case JZ_LCD_TYPE_SPECIAL_TFT_2: case JZ_LCD_TYPE_SPECIAL_TFT_3: writel(pdata->special_tft_config.spl, jzfb->base + JZ_REG_LCD_SPL); writel(pdata->special_tft_config.cls, jzfb->base + JZ_REG_LCD_CLS); writel(pdata->special_tft_config.ps, jzfb->base + JZ_REG_LCD_PS); writel(pdata->special_tft_config.ps, jzfb->base + JZ_REG_LCD_REV); break; default: cfg |= JZ_LCD_CFG_PS_DISABLE; cfg |= JZ_LCD_CFG_CLS_DISABLE; cfg |= JZ_LCD_CFG_SPL_DISABLE; cfg |= JZ_LCD_CFG_REV_DISABLE; break; } writel(mode->hsync_len, jzfb->base + JZ_REG_LCD_HSYNC); writel(mode->vsync_len, jzfb->base + JZ_REG_LCD_VSYNC); writel((ht << 16) | vt, jzfb->base + JZ_REG_LCD_VAT); writel((hds << 16) | hde, jzfb->base + JZ_REG_LCD_DAH); writel((vds << 16) | vde, jzfb->base + JZ_REG_LCD_DAV); writel(cfg, jzfb->base + JZ_REG_LCD_CFG); writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); if (!jzfb->is_enabled) clk_disable(jzfb->ldclk); mutex_unlock(&jzfb->lock); clk_set_rate(jzfb->lpclk, rate); clk_set_rate(jzfb->ldclk, rate * 3); return 0; } static void jzfb_enable(struct jzfb *jzfb) { uint32_t ctrl; clk_enable(jzfb->ldclk); jz_gpio_bulk_resume(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); jz_gpio_bulk_resume(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); writel(0, jzfb->base + JZ_REG_LCD_STATE); writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); ctrl |= JZ_LCD_CTRL_ENABLE; ctrl &= ~JZ_LCD_CTRL_DISABLE; writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); } static void jzfb_disable(struct jzfb *jzfb) { uint32_t ctrl; ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); ctrl |= JZ_LCD_CTRL_DISABLE; writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); do { ctrl = readl(jzfb->base + JZ_REG_LCD_STATE); } while (!(ctrl & JZ_LCD_STATE_DISABLED)); jz_gpio_bulk_suspend(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); jz_gpio_bulk_suspend(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); clk_disable(jzfb->ldclk); } static int jzfb_blank(int blank_mode, struct fb_info *info) { struct jzfb *jzfb = info->par; switch (blank_mode) { case FB_BLANK_UNBLANK: mutex_lock(&jzfb->lock); if (jzfb->is_enabled) { mutex_unlock(&jzfb->lock); return 0; } jzfb_enable(jzfb); jzfb->is_enabled = 1; mutex_unlock(&jzfb->lock); break; default: mutex_lock(&jzfb->lock); if (!jzfb->is_enabled) { mutex_unlock(&jzfb->lock); return 0; } jzfb_disable(jzfb); jzfb->is_enabled = 0; mutex_unlock(&jzfb->lock); break; } return 0; } static int jzfb_alloc_devmem(struct jzfb *jzfb) { int max_videosize = 0; struct fb_videomode *mode = jzfb->pdata->modes; void *page; int i; for (i = 0; i < jzfb->pdata->num_modes; ++mode, ++i) { if (max_videosize < mode->xres * mode->yres) max_videosize = mode->xres * mode->yres; } max_videosize *= jzfb_get_controller_bpp(jzfb) >> 3; jzfb->framedesc = dma_alloc_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), &jzfb->framedesc_phys, GFP_KERNEL); if (!jzfb->framedesc) return -ENOMEM; jzfb->vidmem_size = PAGE_ALIGN(max_videosize); jzfb->vidmem = dma_alloc_coherent(&jzfb->pdev->dev, jzfb->vidmem_size, &jzfb->vidmem_phys, GFP_KERNEL); if (!jzfb->vidmem) goto err_free_framedesc; for (page = jzfb->vidmem; page < jzfb->vidmem + PAGE_ALIGN(jzfb->vidmem_size); page += PAGE_SIZE) { SetPageReserved(virt_to_page(page)); } jzfb->framedesc->next = jzfb->framedesc_phys; jzfb->framedesc->addr = jzfb->vidmem_phys; jzfb->framedesc->id = 0xdeafbead; jzfb->framedesc->cmd = 0; jzfb->framedesc->cmd |= max_videosize / 4; return 0; err_free_framedesc: dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), jzfb->framedesc, jzfb->framedesc_phys); return -ENOMEM; } static void jzfb_free_devmem(struct jzfb *jzfb) { dma_free_coherent(&jzfb->pdev->dev, jzfb->vidmem_size, jzfb->vidmem, jzfb->vidmem_phys); dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), jzfb->framedesc, jzfb->framedesc_phys); } static struct fb_ops jzfb_ops = { .owner = THIS_MODULE, .fb_check_var = jzfb_check_var, .fb_set_par = jzfb_set_par, .fb_blank = jzfb_blank, .fb_fillrect = sys_fillrect, .fb_copyarea = sys_copyarea, .fb_imageblit = sys_imageblit, .fb_setcolreg = jzfb_setcolreg, }; static int jzfb_probe(struct platform_device *pdev) { int ret; struct jzfb *jzfb; struct fb_info *fb; struct jz4740_fb_platform_data *pdata = pdev->dev.platform_data; struct resource *mem; if (!pdata) { dev_err(&pdev->dev, "Missing platform data\n"); return -ENXIO; } fb = framebuffer_alloc(sizeof(struct jzfb), &pdev->dev); if (!fb) { dev_err(&pdev->dev, "Failed to allocate framebuffer device\n"); return -ENOMEM; } fb->fbops = &jzfb_ops; fb->flags = FBINFO_DEFAULT; jzfb = fb->par; jzfb->pdev = pdev; jzfb->pdata = pdata; jzfb->ldclk = devm_clk_get(&pdev->dev, "lcd"); if (IS_ERR(jzfb->ldclk)) { ret = PTR_ERR(jzfb->ldclk); dev_err(&pdev->dev, "Failed to get lcd clock: %d\n", ret); goto err_framebuffer_release; } jzfb->lpclk = devm_clk_get(&pdev->dev, "lcd_pclk"); if (IS_ERR(jzfb->lpclk)) { ret = PTR_ERR(jzfb->lpclk); dev_err(&pdev->dev, "Failed to get lcd pixel clock: %d\n", ret); goto err_framebuffer_release; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); jzfb->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(jzfb->base)) { ret = PTR_ERR(jzfb->base); goto err_framebuffer_release; } platform_set_drvdata(pdev, jzfb); mutex_init(&jzfb->lock); fb_videomode_to_modelist(pdata->modes, pdata->num_modes, &fb->modelist); fb_videomode_to_var(&fb->var, pdata->modes); fb->var.bits_per_pixel = pdata->bpp; jzfb_check_var(&fb->var, fb); ret = jzfb_alloc_devmem(jzfb); if (ret) { dev_err(&pdev->dev, "Failed to allocate video memory\n"); goto err_framebuffer_release; } fb->fix = jzfb_fix; fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8; fb->fix.mmio_start = mem->start; fb->fix.mmio_len = resource_size(mem); fb->fix.smem_start = jzfb->vidmem_phys; fb->fix.smem_len = fb->fix.line_length * fb->var.yres; fb->screen_base = jzfb->vidmem; fb->pseudo_palette = jzfb->pseudo_palette; fb_alloc_cmap(&fb->cmap, 256, 0); clk_enable(jzfb->ldclk); jzfb->is_enabled = 1; writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); fb->mode = NULL; jzfb_set_par(fb); jz_gpio_bulk_request(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); jz_gpio_bulk_request(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); ret = register_framebuffer(fb); if (ret) { dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret); goto err_free_devmem; } jzfb->fb = fb; return 0; err_free_devmem: jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); fb_dealloc_cmap(&fb->cmap); jzfb_free_devmem(jzfb); err_framebuffer_release: framebuffer_release(fb); return ret; } static int jzfb_remove(struct platform_device *pdev) { struct jzfb *jzfb = platform_get_drvdata(pdev); jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb); jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); fb_dealloc_cmap(&jzfb->fb->cmap); jzfb_free_devmem(jzfb); framebuffer_release(jzfb->fb); return 0; } #ifdef CONFIG_PM static int jzfb_suspend(struct device *dev) { struct jzfb *jzfb = dev_get_drvdata(dev); console_lock(); fb_set_suspend(jzfb->fb, 1); console_unlock(); mutex_lock(&jzfb->lock); if (jzfb->is_enabled) jzfb_disable(jzfb); mutex_unlock(&jzfb->lock); return 0; } static int jzfb_resume(struct device *dev) { struct jzfb *jzfb = dev_get_drvdata(dev); clk_enable(jzfb->ldclk); mutex_lock(&jzfb->lock); if (jzfb->is_enabled) jzfb_enable(jzfb); mutex_unlock(&jzfb->lock); console_lock(); fb_set_suspend(jzfb->fb, 0); console_unlock(); return 0; } static const struct dev_pm_ops jzfb_pm_ops = { .suspend = jzfb_suspend, .resume = jzfb_resume, .poweroff = jzfb_suspend, .restore = jzfb_resume, }; #define JZFB_PM_OPS (&jzfb_pm_ops) #else #define JZFB_PM_OPS NULL #endif static struct platform_driver jzfb_driver = { .probe = jzfb_probe, .remove = jzfb_remove, .driver = { .name = "jz4740-fb", .pm = JZFB_PM_OPS, }, }; static int __init jzfb_init(void) { return platform_driver_register(&jzfb_driver); } module_init(jzfb_init); static void __exit jzfb_exit(void) { platform_driver_unregister(&jzfb_driver); } module_exit(jzfb_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("JZ4740 SoC LCD framebuffer driver"); MODULE_ALIAS("platform:jz4740-fb");
arch/mips/jz4770/board-gcw0.c
/* * board-gcw0.c - GCW Zero: JZ4770-based handheld game console * * File based on Pisces board definition. * Copyright (C) 2006-2008, Ingenic Semiconductor Inc. * Original author: <jlwei@ingenic.cn> * * GCW Zero specific changes: * Copyright (C) 2012, Maarten ter Huurne <maarten@treewalker.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/init.h> #include <linux/sched.h> #include <linux/ioport.h> #include <linux/mm.h> #include <linux/console.h> #include <linux/delay.h> #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/i2c-gpio.h> #include <linux/input.h> #include <linux/gpio_keys.h> #include <linux/leds.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/pwm_backlight.h> #include <asm/cpu.h> #include <asm/bootinfo.h> #include <asm/mipsregs.h> #include <asm/reboot.h> #include <linux/mmc/host.h> #include <linux/act8600_power.h> #include <linux/platform_data/jz4770_fb.h> #include <linux/platform_data/linkdev.h> #include <linux/platform_data/mxc6225.h> #include <linux/platform_data/pwm-haptic.h> #include <linux/platform_data/usb-musb-jz4770.h> #include <linux/pinctrl/machine.h> #include <linux/power/gpio-charger.h> #include <linux/power/jz4770-battery.h> #include <linux/regulator/fixed.h> #include <linux/regulator/machine.h> #include <linux/rfkill-regulator.h> #include <linux/usb/musb.h> #include <media/radio-rda5807.h> #include <sound/jz4770.h> #include <video/jzpanel.h> #ifdef CONFIG_PANEL_HX8347A01 #include <video/panel-hx8347a01.h> #else #include <video/panel-nt39016.h> #endif #include <asm/mach-jz4770/board-gcw0.h> #include <asm/mach-jz4770/gpio.h> #include <asm/mach-jz4770/jz4770i2c.h> #include <asm/mach-jz4770/jz4770misc.h> #include <asm/mach-jz4770/mmc.h> #include <asm/mach-jz4770/platform.h> #include "clock.h" /* Video */ #define GPIO_PANEL_SOMETHING JZ_GPIO_PORTF(0) static int gcw0_panel_init(void **out_panel, struct device *dev, void *panel_pdata) { int ret; #ifdef CONFIG_PANEL_HX8347A01 ret = hx8347a01_panel_ops.init(out_panel, dev, panel_pdata); #else ret = nt39016_panel_ops.init(out_panel, dev, panel_pdata); #endif if (ret) return ret; ret = devm_gpio_request(dev, GPIO_PANEL_SOMETHING, "LCD panel unknown"); if (ret) { dev_err(dev, "Failed to request LCD panel unknown pin: %d\n", ret); return ret; } gpio_direction_output(GPIO_PANEL_SOMETHING, 1); return 0; } static void gcw0_panel_exit(void *panel) { #ifdef CONFIG_PANEL_HX8347A01 hx8347a01_panel_ops.exit(panel); #else nt39016_panel_ops.exit(panel); #endif } static void gcw0_panel_enable(void *panel) { act8600_output_enable(6, true); #ifdef CONFIG_PANEL_HX8347A01 hx8347a01_panel_ops.enable(panel); #else nt39016_panel_ops.enable(panel); #endif } static void gcw0_panel_disable(void *panel) { #ifdef CONFIG_PANEL_HX8347A01 hx8347a01_panel_ops.disable(panel); #else nt39016_panel_ops.disable(panel); #endif act8600_output_enable(6, false); } #ifdef CONFIG_PANEL_HX8347A01 static struct hx8347a01_platform_data gcw0_panel_pdata = { #else static struct nt39016_platform_data gcw0_panel_pdata = { #endif .gpio_reset = JZ_GPIO_PORTE(2), .gpio_clock = JZ_GPIO_PORTE(15), .gpio_enable = JZ_GPIO_PORTE(16), .gpio_data = JZ_GPIO_PORTE(17), }; static struct panel_ops gcw0_panel_ops = { .init = gcw0_panel_init, .exit = gcw0_panel_exit, .enable = gcw0_panel_enable, .disable = gcw0_panel_disable, }; static struct jzfb_platform_data gcw0_fb_pdata = { .panel_ops = &gcw0_panel_ops, .panel_pdata = &gcw0_panel_pdata, }; /* Buttons */ static struct gpio_keys_button gcw0_buttons[] = { /* D-pad up */ { .gpio = JZ_GPIO_PORTE(21), .active_low = 1, .code = KEY_UP, .debounce_interval = 10, }, /* D-pad down */ { .gpio = JZ_GPIO_PORTE(25), .active_low = 1, .code = KEY_DOWN, .debounce_interval = 10, }, /* D-pad left */ { .gpio = JZ_GPIO_PORTE(23), .active_low = 1, .code = KEY_LEFT, .debounce_interval = 10, }, /* D-pad right */ { .gpio = JZ_GPIO_PORTE(24), .active_low = 1, .code = KEY_RIGHT, .debounce_interval = 10, }, /* A button */ { .gpio = JZ_GPIO_PORTE(29), .active_low = 1, .code = KEY_LEFTCTRL, .debounce_interval = 10, }, /* B button */ { .gpio = JZ_GPIO_PORTE(20), .active_low = 1, .code = KEY_LEFTALT, .debounce_interval = 10, }, /* Top button (labeled Y, should be X) */ { .gpio = JZ_GPIO_PORTE(27), .active_low = 1, .code = KEY_SPACE, .debounce_interval = 10, }, /* Left button (labeled X, should be Y) */ { .gpio = JZ_GPIO_PORTE(28), .active_low = 1, .code = KEY_LEFTSHIFT, .debounce_interval = 10, }, /* Left shoulder button */ { .gpio = JZ_GPIO_PORTB(20), .active_low = 1, .code = KEY_TAB, .debounce_interval = 10, }, /* Right shoulder button */ { .gpio = JZ_GPIO_PORTE(26), .active_low = 1, .code = KEY_BACKSPACE, .debounce_interval = 10, }, /* START button */ { .gpio = JZ_GPIO_PORTB(21), .active_low = 1, .code = KEY_ENTER, .debounce_interval = 10, }, /* SELECT button */ { .gpio = JZ_GPIO_PORTD(18), /* This is the only button that is active high, * since it doubles as BOOT_SEL1. */ .active_low = 0, .code = KEY_ESC, .debounce_interval = 10, }, /* POWER slider */ { .gpio = JZ_GPIO_PORTA(30), .active_low = 1, .code = KEY_POWER, .debounce_interval = 10, .wakeup = 1, }, /* POWER hold */ { .gpio = JZ_GPIO_PORTF(11), .active_low = 1, .code = KEY_PAUSE, .debounce_interval = 10, }, }; static struct gpio_keys_platform_data gcw0_gpio_keys_pdata = { .buttons = gcw0_buttons, .nbuttons = ARRAY_SIZE(gcw0_buttons), .rep = 1, }; static struct platform_device gcw0_gpio_keys_device = { .name = "gpio-keys", .id = -1, .dev = { .platform_data = &gcw0_gpio_keys_pdata, }, }; /* SD cards */ static struct jz_mmc_platform_data gcw_internal_sd_data = { .support_sdio = 0, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, .bus_width = 4, .gpio_card_detect = -1, .gpio_read_only = -1, .gpio_power = -1, .nonremovable = 1, }; static struct jz_mmc_platform_data gcw_external_sd_data = { .support_sdio = 0, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, .bus_width = 4, .gpio_card_detect = JZ_GPIO_PORTB(2), .card_detect_active_low = 1, .gpio_read_only = -1, .gpio_power = JZ_GPIO_PORTE(9), .power_active_low = 1, }; /* FM radio receiver */ static struct rda5807_platform_data gcw0_rda5807_pdata = { .input_flags = RDA5807_INPUT_LNA_WC_25 | RDA5807_LNA_PORT_P, .output_flags = RDA5807_OUTPUT_AUDIO_ANALOG, }; /* Power Management Unit */ static struct act8600_outputs_t act8600_outputs[] = { { 4, 0x57, true }, /* USB OTG: 5.3V */ { 5, 0x31, true }, /* AVD: 2.5V */ { 6, 0x39, false }, /* LCD: 3.3V */ { 7, 0x39, true }, /* generic: 3.3V */ { 8, 0x24, true }, /* generic: 1.8V */ }; static struct act8600_platform_pdata_t act8600_platform_pdata = { .outputs = act8600_outputs, .nr_outputs = ARRAY_SIZE(act8600_outputs), }; /* Battery */ static struct jz_battery_platform_data gcw0_battery_pdata = { .gpio_charge = -1, //.gpio_charge_active_low = 0, .info = { .name = "battery", .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, .voltage_max_design = 5700000, .voltage_min_design = 4600000, }, }; /* Charger */ #define GPIO_DC_CHARGER JZ_GPIO_PORTF(5) #define GPIO_USB_CHARGER JZ_GPIO_PORTB(5) static char *gcw0_batteries[] = { "battery", }; static struct gpio_charger_platform_data gcw0_dc_charger_pdata = { .name = "dc", .type = POWER_SUPPLY_TYPE_MAINS, .gpio = GPIO_DC_CHARGER, .supplied_to = gcw0_batteries, .num_supplicants = ARRAY_SIZE(gcw0_batteries), }; static struct platform_device gcw0_dc_charger_device = { .name = "gpio-charger", .id = 0, .dev = { .platform_data = &gcw0_dc_charger_pdata, }, }; static struct gpio_charger_platform_data gcw0_usb_charger_pdata = { .name = "usb", .type = POWER_SUPPLY_TYPE_USB, .gpio = GPIO_USB_CHARGER, .supplied_to = gcw0_batteries, .num_supplicants = ARRAY_SIZE(gcw0_batteries), }; static struct platform_device gcw0_usb_charger_device = { .name = "gpio-charger", .id = 1, .dev = { .platform_data = &gcw0_usb_charger_pdata, }, }; /* USB 1.1 Host (OHCI) */ static struct regulator_consumer_supply gcw0_internal_usb_regulator_consumer = REGULATOR_SUPPLY("vrfkill", "rfkill-regulator.0"); static struct regulator_init_data gcw0_internal_usb_regulator_init_data = { .num_consumer_supplies = 1, .consumer_supplies = &gcw0_internal_usb_regulator_consumer, .constraints = { .name = "USB power", .min_uV = 3300000, .max_uV = 3300000, .valid_modes_mask = REGULATOR_MODE_NORMAL, .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, }; static struct fixed_voltage_config gcw0_internal_usb_regulator_data = { .supply_name = "USB power", .microvolts = 3300000, .gpio = JZ_GPIO_PORTF(10), .init_data = &gcw0_internal_usb_regulator_init_data, }; static struct platform_device gcw0_internal_usb_regulator_device = { .name = "reg-fixed-voltage", .id = -1, .dev = { .platform_data = &gcw0_internal_usb_regulator_data, } }; /* USB OTG (musb) */ #define GPIO_USB_OTG_ID_PIN JZ_GPIO_PORTF(18) static struct jz_otg_board_data gcw0_otg_board_data = { .gpio_id_pin = GPIO_USB_OTG_ID_PIN, .gpio_id_debounce_ms = 500, }; /* I2C devices */ /* * Select which I2C busses use a hardware adapter (i2c-jz4770) and which use * a software adapter (i2c-gpio). */ #if defined(CONFIG_I2C_JZ4770) #define I2C0_USE_HW 1 #define I2C1_USE_HW 1 #else #define I2C0_USE_HW 0 #define I2C1_USE_HW 0 #endif static struct i2c_board_info gcw0_i2c0_devs[] __initdata = { { .type = "radio-rda5807", .addr = RDA5807_I2C_ADDR, .platform_data = &gcw0_rda5807_pdata, }, }; /* We don't have a use for the INT pin yet. */ #define GPIO_MXC6225_INT JZ_GPIO_PORTF(13) static struct i2c_board_info gcw0_i2c1_devs[] __initdata = { { .type = "mxc6225", .addr = MXC6225_I2C_ADDR, }, }; static struct i2c_board_info gcw0_i2c3_devs[] __initdata = { { .type = ACT8600_NAME, .addr = ACT8600_I2C_ADDR, .platform_data = &act8600_platform_pdata, }, }; static struct i2c_board_info gcw0_i2c4_devs[] __initdata = { /* the IT6610 is on this bus, but we don't have a driver for it */ }; /* I2C busses */ static struct i2c_jz4770_platform_data gcw0_i2c0_platform_data __initdata = { .use_dma = false, }; static struct i2c_jz4770_platform_data gcw0_i2c1_platform_data __initdata = { .use_dma = false, }; #if I2C0_USE_HW == 0 static struct i2c_gpio_platform_data gcw0_i2c0_gpio_data = { .sda_pin = JZ_GPIO_PORTD(30), .scl_pin = JZ_GPIO_PORTD(31), .udelay = 2, /* 250 kHz */ }; static struct platform_device gcw0_i2c0_gpio_device = { .name = "i2c-gpio", .id = 0, .dev = { .platform_data = &gcw0_i2c0_gpio_data, }, }; #endif #if I2C1_USE_HW == 0 static struct i2c_gpio_platform_data gcw0_i2c1_gpio_data = { .sda_pin = JZ_GPIO_PORTE(30), .scl_pin = JZ_GPIO_PORTE(31), .udelay = 2, /* 250 kHz */ }; static struct platform_device gcw0_i2c1_gpio_device = { .name = "i2c-gpio", .id = 1, .dev = { .platform_data = &gcw0_i2c1_gpio_data, }, }; #endif static struct i2c_gpio_platform_data gcw0_i2c3_gpio_data = { .sda_pin = JZ_GPIO_PORTD(5), .scl_pin = JZ_GPIO_PORTD(4), .udelay = 2, /* 250 kHz */ }; static struct platform_device gcw0_i2c3_gpio_device = { .name = "i2c-gpio", .id = 3, .dev = { .platform_data = &gcw0_i2c3_gpio_data, }, }; static struct i2c_gpio_platform_data gcw0_i2c4_gpio_data = { .sda_pin = JZ_GPIO_PORTD(6), .scl_pin = JZ_GPIO_PORTD(7), .udelay = 5, /* 100 kHz */ }; static struct platform_device gcw0_i2c4_gpio_device = { .name = "i2c-gpio", .id = 4, .dev = { .platform_data = &gcw0_i2c4_gpio_data, }, }; /* LCD backlight */ static struct platform_pwm_backlight_data gcw0_backlight_pdata = { .polarity = PWM_POLARITY_NORMAL, .max_brightness = 255, .dft_brightness = 145, .pwm_period_ns = 40000, /* 25 kHz: outside human hearing range */ }; static struct platform_device gcw0_backlight_device = { .name = "pwm-backlight", .id = -1, .dev = { .platform_data = &gcw0_backlight_pdata, }, }; /* Audio */ static struct jz4770_icdc_platform_data gcw0_icdc_pdata = { .mic_mode = JZ4770_MIC_1, }; static struct platform_device gcw0_audio_device = { .name = "gcw0-audio", .id = -1, }; struct jz_clk_board_data jz_clk_bdata = { /* These two are fixed in hardware. */ .ext_rate = 12000000, .rtc_rate = 32768, /* * Pick 432 MHz as it is the least common multiple of 27 MHz (required * by TV encoder) and 48 MHz (required by USB host). */ .pll1_rate = 432000000, }; /* Power LED */ static struct gpio_led gcw0_leds[] = { { .name = "power", .gpio = JZ_GPIO_PORTB(30), .active_low = 1, .default_state = LEDS_GPIO_DEFSTATE_ON, }, }; static struct gpio_led_platform_data gcw0_led_pdata = { .leds = gcw0_leds, .num_leds = ARRAY_SIZE(gcw0_leds), }; static struct platform_device gcw0_led_device = { .name = "leds-gpio", .id = -1, .dev = { .platform_data = &gcw0_led_pdata, }, }; static struct rfkill_regulator_platform_data gcw0_rfkill_pdata = { .name = "gcw0-wifi", .type = RFKILL_TYPE_WLAN, }; static struct platform_device gcw0_rfkill_device = { .name = "rfkill-regulator", .id = 0, .dev = { .platform_data = &gcw0_rfkill_pdata, }, }; static const char * gcw0_joystick_gpiokeys_whitelist[] = { "evdev", }; static const struct linkdev_pdata_device_info gcw0_joystick_devices[] = { { .name = "analog joystick", }, { .name = "gpio-keys", .handlers_whitelist = gcw0_joystick_gpiokeys_whitelist, .nb_handlers = ARRAY_SIZE(gcw0_joystick_gpiokeys_whitelist), }, }; static const struct linkdev_pdata_key_map gcw0_key_map[] = { { .code = KEY_UP, .event = { .type = EV_ABS, .code = ABS_HAT0Y, .value = -1, }, }, { .code = KEY_DOWN, .event = { .type = EV_ABS, .code = ABS_HAT0Y, .value = 1, } }, { .code = KEY_LEFT, .event = { .type = EV_ABS, .code = ABS_HAT0X, .value = -1, }, }, { .code = KEY_RIGHT, .event = { .type = EV_ABS, .code = ABS_HAT0X, .value = 1, }, }, { .code = KEY_LEFTCTRL, .event.code = BTN_EAST, }, { .code = KEY_LEFTALT, .event.code = BTN_SOUTH, }, { .code = KEY_LEFTSHIFT, .event.code = BTN_WEST, }, { .code = KEY_SPACE, .event.code = BTN_NORTH, }, { .code = KEY_ENTER, .event.code = BTN_START, }, { .code = KEY_ESC, .event.code = BTN_SELECT, }, { .code = KEY_TAB, .event.code = BTN_THUMBL, }, { .code = KEY_BACKSPACE, .event.code = BTN_THUMBR, }, }; static const struct linkdev_pdata_abs_map gcw0_abs_map[] = { { .name = "analog joystick", .axis = ABS_X, .axis_dest = ABS_X, }, { .name = "analog joystick", .axis = ABS_Y, .axis_dest = ABS_Y, }, { .name = "gpio-keys", .axis = ABS_HAT0X, .axis_dest = ABS_HAT0X, }, { .name = "gpio-keys", .axis = ABS_HAT0Y, .axis_dest = ABS_HAT0Y, }, }; static struct linkdev_platform_data gcw0_joystick_pdata = { /* This specific name informs SDL about the composition of the joystick */ .name = "linkdev device (Analog 2-axis 8-button 2-hat)", .devices = gcw0_joystick_devices, .nb_devices = ARRAY_SIZE(gcw0_joystick_devices), .key_map = gcw0_key_map, .key_map_size = ARRAY_SIZE(gcw0_key_map), .abs_map = gcw0_abs_map, .abs_map_size = ARRAY_SIZE(gcw0_abs_map), }; /* GCW0 Input driver */ static struct platform_device gcw0_joystick_device = { .name = "linkdev", .id = -1, .dev = { .platform_data = &gcw0_joystick_pdata, }, }; static struct pwm_haptic_platform_data gcw0_haptic_pdata = { .pwm_period_ns = 2000000, }; /* Rumble device */ static struct platform_device gcw0_haptic_device = { .name = "pwm-haptic", .id = -1, .dev = { .platform_data = &gcw0_haptic_pdata, }, }; /* Device registration */ static struct platform_device *jz_platform_devices[] __initdata = { &gcw0_internal_usb_regulator_device, &jz4770_usb_ohci_device, &jz4770_usb_otg_xceiv_device, &jz4770_usb_otg_device, &jz4770_lcd_device, &jz4770_i2s_device, &jz4770_pcm_device, &jz4770_icdc_device, #if I2C0_USE_HW == 1 &jz4770_i2c0_device, #endif #if I2C1_USE_HW == 1 &jz4770_i2c1_device, #endif #if I2C0_USE_HW == 0 &gcw0_i2c0_gpio_device, #endif #if I2C1_USE_HW == 0 &gcw0_i2c1_gpio_device, #endif &gcw0_i2c3_gpio_device, &gcw0_i2c4_gpio_device, &jz4770_pwm_device, &jz4770_adc_device, &jz4770_rtc_device, &gcw0_gpio_keys_device, &gcw0_backlight_device, &gcw0_audio_device, &jz4770_msc0_device, &jz4770_msc1_device, &gcw0_led_device, &gcw0_dc_charger_device, &gcw0_usb_charger_device, &jz4770_vpu_device, &gcw0_rfkill_device, &gcw0_joystick_device, &jz4770_wdt_device, &gcw0_haptic_device, }; static int __init gcw0_init_platform_devices(void) { struct musb_hdrc_platform_data *otg_platform_data = jz4770_usb_otg_device.dev.platform_data; otg_platform_data->board_data = &gcw0_otg_board_data; jz4770_lcd_device.dev.platform_data = &gcw0_fb_pdata; jz4770_adc_device.dev.platform_data = &gcw0_battery_pdata; jz4770_msc0_device.dev.platform_data = &gcw_internal_sd_data; jz4770_msc1_device.dev.platform_data = &gcw_external_sd_data; jz4770_icdc_device.dev.platform_data = &gcw0_icdc_pdata; return platform_add_devices(jz_platform_devices, ARRAY_SIZE(jz_platform_devices)); } static void __init board_i2c_init(void) { jz4770_i2c0_device.dev.platform_data = &gcw0_i2c0_platform_data; jz4770_i2c1_device.dev.platform_data = &gcw0_i2c1_platform_data; i2c_register_board_info(0, gcw0_i2c0_devs, ARRAY_SIZE(gcw0_i2c0_devs)); i2c_register_board_info(1, gcw0_i2c1_devs, ARRAY_SIZE(gcw0_i2c1_devs)); i2c_register_board_info(3, gcw0_i2c3_devs, ARRAY_SIZE(gcw0_i2c3_devs)); i2c_register_board_info(4, gcw0_i2c4_devs, ARRAY_SIZE(gcw0_i2c4_devs)); } static void __init board_gpio_setup(void) { /* SELECT button */ jz_gpio_disable_pullup(JZ_GPIO_PORTD(18)); /* DC power source present (high active) */ jz_gpio_disable_pullup(GPIO_DC_CHARGER); /* USB power source present (high active) */ jz_gpio_disable_pullup(GPIO_USB_CHARGER); /* MXC6225 data sheet says INT should not be pulled up or down */ jz_gpio_disable_pullup(GPIO_MXC6225_INT); } static struct pinctrl_map pin_map[] __initdata = { #if I2C0_USE_HW == 1 PIN_MAP_MUX_GROUP("i2c-jz4770.0", PINCTRL_STATE_DEFAULT, "jz4770-pinctrl", NULL, "i2c0"), #endif #if I2C1_USE_HW == 1 PIN_MAP_MUX_GROUP("i2c-jz4770.1", PINCTRL_STATE_DEFAULT, "jz4770-pinctrl", NULL, "i2c1"), #endif PIN_MAP_MUX_GROUP("jz-msc.0", PINCTRL_STATE_DEFAULT, "jz4770-pinctrl", "msc0_4bit", "msc0"), PIN_MAP_MUX_GROUP("jz-msc.1", PINCTRL_STATE_DEFAULT, "jz4770-pinctrl", "msc1_4bit", "msc1"), /* pwm1: LCD backlight */ PIN_MAP_MUX_GROUP("pwm-backlight", PINCTRL_STATE_DEFAULT, "jz4770-pinctrl", NULL, "pwm1"), /* pwm4: rumble motor */ PIN_MAP_MUX_GROUP("pwm-haptic", PINCTRL_STATE_DEFAULT, "jz4770-pinctrl", NULL, "pwm4"), PIN_MAP_MUX_GROUP("musb-jz.0", PINCTRL_STATE_DEFAULT, "jz4770-pinctrl", NULL, "otg"), PIN_MAP_MUX_GROUP("jz-lcd.0", PINCTRL_STATE_DEFAULT, "jz4770-pinctrl", "lcd_rgb888", "lcd"), PIN_MAP_MUX_GROUP("jz-lcd.0", PINCTRL_STATE_SLEEP, "jz4770-pinctrl", "no_pins", "lcd"), }; static struct pwm_lookup pwm_lookup[] = { PWM_LOOKUP("jz4770-pwm", 1, "pwm-backlight", NULL), PWM_LOOKUP("jz4770-pwm", 4, "pwm-haptic", NULL), }; static void __init board_init_pins(void) { pinctrl_register_mappings(pin_map, ARRAY_SIZE(pin_map)); pwm_add_table(pwm_lookup, ARRAY_SIZE(pwm_lookup)); } static int __init gcw0_board_setup(void) { printk(KERN_INFO "GCW Zero JZ4770 setup\n"); board_init_pins(); board_gpio_setup(); board_i2c_init(); if (gcw0_init_platform_devices()) panic("Failed to initialize platform devices"); return 0; } arch_initcall(gcw0_board_setup);
menuconfig
接著焊接轉板
背面
接著上3D打印的外殼
感動的一刻
正面
下邊
左邊
上面
右邊
周哥屏對比IPS屏(感謝剃頭提供)