diff options
Diffstat (limited to 'arch/arm/mach-mx5/devices_ccwmx51.c')
-rw-r--r-- | arch/arm/mach-mx5/devices_ccwmx51.c | 961 |
1 files changed, 840 insertions, 121 deletions
diff --git a/arch/arm/mach-mx5/devices_ccwmx51.c b/arch/arm/mach-mx5/devices_ccwmx51.c index 4e4a07f7c4a7..bd96da04bc62 100644 --- a/arch/arm/mach-mx5/devices_ccwmx51.c +++ b/arch/arm/mach-mx5/devices_ccwmx51.c @@ -25,6 +25,7 @@ #include <linux/platform_device.h> #include <linux/fsl_devices.h> #include <linux/spi/spi.h> +#include <linux/spi/ads7846.h> #include <linux/i2c.h> #include <linux/ata.h> #include <linux/regulator/consumer.h> @@ -34,6 +35,7 @@ #include <linux/mxcfb.h> #include <linux/pwm_backlight.h> #include <linux/smsc911x.h> +#include <linux/sysfs.h> #include <mach/common.h> #include <mach/hardware.h> #include <asm/irq.h> @@ -45,6 +47,7 @@ #include <mach/gpio.h> #include <mach/mmc.h> #include <mach/mxc_dvfs.h> +#include <video/ad9389.h> #include "board-ccwmx51.h" #include "iomux.h" #include "crm_regs.h" @@ -60,65 +63,159 @@ #include <asm/mach/flash.h> #endif -#if defined(CONFIG_MTD_NAND_MXC) \ - || defined(CONFIG_MTD_NAND_MXC_MODULE) \ - || defined(CONFIG_MTD_NAND_MXC_V2) \ - || defined(CONFIG_MTD_NAND_MXC_V2_MODULE) \ - || defined(CONFIG_MTD_NAND_MXC_V3) \ - || defined(CONFIG_MTD_NAND_MXC_V3_MODULE) +#if defined(CONFIG_VIDEO_AD9389) || defined(CONFIG_VIDEO_AD9389_MODULE) +static int debug = 0; +#endif -extern void gpio_nand_active(void); -extern void gpio_nand_inactive(void); +#define AD9389_DBG 0x0001 +#define DBG(flag, fmt, args...) do { \ + if (debug & flag) \ + printk(fmt, ## args); \ + } while (0) -static int nand_init(void) +static u8 ccwmx51_mod_variant = 0; +static u8 ccwmx51_mod_rev = 0; +static u32 ccwmx51_mod_sn = 0; +static u8 ccwmx51_bb_rev = BASE_BOARD_REV; + +void ccwmx51_set_mod_variant(u8 variant) { - /* Configure the pins */ - gpio_nand_active(); - return 0; + ccwmx51_mod_variant = variant; +} +void ccwmx51_set_mod_revision(u8 revision) +{ + ccwmx51_mod_rev = revision; +} +void ccwmx51_set_mod_sn(u32 sn) +{ + ccwmx51_mod_sn = sn; } -static void nand_exit(void) +#ifdef CONFIG_SYSFS +static ssize_t ccwmx51_mod_variant_attr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) { - /* Free the pins */ - gpio_nand_inactive(); + return snprintf(buf, PAGE_SIZE, "%d\n", ccwmx51_mod_variant); } -struct flash_platform_data mxc_nand_data = { - .width = 1, - .init = nand_init, - .exit = nand_exit, -}; -#endif +static ssize_t ccwmx51_mod_rev_attr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ccwmx51_mod_rev); +} -#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE) -struct smsc911x_platform_config ccwmx51_smsc9118 = { - .flags = SMSC911X_USE_32BIT, - .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, - .irq_type = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH, /* push-pull irq */ -}; -#endif +static ssize_t ccwmx51_mod_sn_attr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ccwmx51_mod_sn); +} + +static ssize_t cccwmx51_bb_rev_attr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ccwmx51_bb_rev); +} + +static struct kobj_attribute ccwmx51_mod_variant_attr = + __ATTR(mod_variant, S_IRUGO, ccwmx51_mod_variant_attr_show, NULL); +static struct kobj_attribute ccwmx51_mod_rev_attr = + __ATTR(mod_rev, S_IRUGO, ccwmx51_mod_rev_attr_show, NULL); +static struct kobj_attribute ccwmx51_mod_sn_attr = + __ATTR(mod_sn, S_IRUGO, ccwmx51_mod_sn_attr_show, NULL); +static struct kobj_attribute ccwmx51_bb_rev_attr = + __ATTR(bb_rev, S_IRUGO, cccwmx51_bb_rev_attr_show, NULL); + +int ccwmx51_create_sysfs_entries(void) +{ + struct kobject *ccwmx51_kobj; + int ret; + + ccwmx51_kobj = kobject_create_and_add("ccwmx51", kernel_kobj); + if (!ccwmx51_kobj) { + printk(KERN_WARNING "kobject_create_and_add ccwmx51 failed\n"); + return -EINVAL; + } + + ret = sysfs_create_file(ccwmx51_kobj, &ccwmx51_mod_variant_attr.attr); + if (ret) { + printk(KERN_ERR + "Unable to register sysdev entry for ccwmx51 hardware variant\n"); + return ret; + } + ret = sysfs_create_file(ccwmx51_kobj, &ccwmx51_mod_rev_attr.attr); + if (ret) { + printk(KERN_ERR + "Unable to register sysdev entry for ccwmx51 hardware revision\n"); + return ret; + } + ret = sysfs_create_file(ccwmx51_kobj, &ccwmx51_mod_sn_attr.attr); + if (ret) { + printk(KERN_ERR + "Unable to register sysdev entry for ccwmx51 hardware SN\n"); + return ret; + } + ret = sysfs_create_file(ccwmx51_kobj, &ccwmx51_bb_rev_attr.attr); + if (ret) { + printk(KERN_ERR + "Unable to register sysdev entry for ccwmx51 base board hardware revision\n"); + return ret; + } + + return 0; +} +#endif /* CONFIG_SYSFS */ + +#if defined(CONFIG_HAS_EARLY_USER_LEDS) +void ccwmx51_user_led(int led, int val) +{ + __iomem void *base; + u32 reg, mask; + + if (led == 1) + mask = 1 << 10; + else if (led == 2) + mask = 1 << 9; + else + return; + + base = ioremap(GPIO3_BASE_ADDR, SZ_4K); + reg = __raw_readl(base); + + if (val) + reg |= mask; + else + reg &= ~mask; + + __raw_writel(reg, base); + iounmap(base); +} +#endif /* CONFIG_HAS_EARLY_USER_LEDS */ #if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE) static int sdhc_write_protect(struct device *dev) { - unsigned short rc = 0; + unsigned short rc = 0; - if (to_platform_device(dev)->id == 0) - rc = 0; /* Not supported WP on JSK board, therefore write is enabled */ - else if (to_platform_device(dev)->id == 2) - rc = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_NANDF_CS1)); - return rc; + if (to_platform_device(dev)->id == 0) + rc = 0; /* Not supported WP on JSK board, therefore write is enabled */ + else if (to_platform_device(dev)->id == 2) + rc = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_NANDF_CS1)); + return rc; } static unsigned int sdhc_get_card_det_status(struct device *dev) { - int ret = 0; + int ret = 1; - if (to_platform_device(dev)->id == 0) - ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_0)); - else if (to_platform_device(dev)->id == 2) - ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO_NAND)); - return ret; + if (to_platform_device(dev)->id == 0) +#ifdef CONFIG_JSCCWMX51_V1 + ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_0)); +#else + ret = 0; +#endif + else if (to_platform_device(dev)->id == 2) + ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO_NAND)); + return ret; } struct mxc_mmc_platform_data mmc1_data = { @@ -135,7 +232,7 @@ struct mxc_mmc_platform_data mmc1_data = { struct mxc_mmc_platform_data mmc3_data = { .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | - MMC_VDD_31_32, + MMC_VDD_31_32, .caps = MMC_CAP_4_BIT_DATA, .min_clk = 150000, .max_clk = 50000000, @@ -145,12 +242,73 @@ struct mxc_mmc_platform_data mmc3_data = { .clock_mmc = "esdhc_clk", .power_mmc = NULL, }; + +void ccwmx51_register_sdio(int interface) +{ + switch (interface) { + case 0: + mxcsdhc1_device.resource[2].start = CCWMX51_SD1_CD_IRQ; + mxcsdhc1_device.resource[2].end = CCWMX51_SD1_CD_IRQ; + mxc_register_device(&mxcsdhc1_device, &mmc1_data); + break; + case 2: + mxcsdhc3_device.resource[2].start = IOMUX_TO_IRQ(MX51_PIN_GPIO_NAND); + mxcsdhc3_device.resource[2].end = IOMUX_TO_IRQ(MX51_PIN_GPIO_NAND); + mxc_register_device(&mxcsdhc3_device, &mmc3_data); + break; + } +} +#endif + +#if defined(CONFIG_MTD_NAND_MXC) \ + || defined(CONFIG_MTD_NAND_MXC_MODULE) \ + || defined(CONFIG_MTD_NAND_MXC_V2) \ + || defined(CONFIG_MTD_NAND_MXC_V2_MODULE) \ + || defined(CONFIG_MTD_NAND_MXC_V3) \ + || defined(CONFIG_MTD_NAND_MXC_V3_MODULE) + +extern void gpio_nand_active(void); +extern void gpio_nand_inactive(void); + +static int nand_init(void) +{ + /* Configure the pins */ + gpio_nand_active(); + return 0; +} + +static void nand_exit(void) +{ + /* Free the pins */ + gpio_nand_inactive(); +} + +struct flash_platform_data mxc_nand_data = { + .width = 1, + .init = nand_init, + .exit = nand_exit, +}; +#endif + +#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE) +struct smsc911x_platform_config ccwmx51_smsc9118 = { + .flags = SMSC911X_USE_32BIT, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH, /* push-pull irq */ +}; #endif #if defined(CONFIG_FB_MXC_SYNC_PANEL) || defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE) -struct resource mxcfb_resources[1] = { +struct resource mxcfb_resources[2] = { { - .flags = IORESOURCE_MEM, + .flags = IORESOURCE_MEM, + .start = 0, + .end = 0, + }, + { + .flags = IORESOURCE_MEM, + .start = 0, + .end = 0, }, }; #endif @@ -158,13 +316,24 @@ struct resource mxcfb_resources[1] = { static struct i2c_board_info ccwmx51_i2c_devices[] __initdata = { #if defined(CONFIG_INPUT_MMA7455L) || defined(CONFIG_INPUT_MMA7455L_MODULE) { - I2C_BOARD_INFO("mma7455l", 0x1d), + I2C_BOARD_INFO("mma7455l", 0x1d), .irq = IOMUX_TO_IRQ(MX51_PIN_GPIO1_7), }, #endif #if defined(CONFIG_SND_SOC_IMX_CCWMX51_WM8753) || defined(CONFIG_SND_SOC_IMX_CCWMX51_WM8753_MODULE) { - I2C_BOARD_INFO("wm8753", 0x1A), + I2C_BOARD_INFO("wm8753", 0x1A), + }, +#endif + +#if defined (CONFIG_MXC_CAMERA_MICRON111_1) || defined(CONFIG_MXC_CAMERA_MICRON111_1_MODULE) + { + I2C_BOARD_INFO("mt9v111_1", 0xB8>>1), + }, +#endif +#if defined (CONFIG_MXC_CAMERA_MICRON111_2) || defined(CONFIG_MXC_CAMERA_MICRON111_2_MODULE) + { + I2C_BOARD_INFO("mt9v111_2", 0x90>>1), }, #endif }; @@ -183,16 +352,54 @@ struct mxc_i2c_platform_data mxci2c_hs_data = { }; #if defined(CONFIG_SPI_MXC_SELECT1_SS1) && (defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE)) +#if defined(CONFIG_CCWMX51_SECOND_TOUCH) +static int touch_pendown_state(void) +{ + return gpio_get_value(IOMUX_TO_GPIO(SECOND_TS_IRQ_PIN)) ? 0 : 1; +} + +static struct ads7846_platform_data ccwmx51js_touch_data = { + .model = 7843, + .x_min = 0, + .y_min = 0, + .x_max = 4095, + .y_max = 4095, + .get_pendown_state = touch_pendown_state, + .buflen = 10, + .skip_samples = 2, + .rotate = 0, +}; + +static struct spi_board_info ccwmx51_2nd_touch[] = { + { + .modalias = "ads7846", + .max_speed_hz = 500000, + .irq = IOMUX_TO_IRQ(SECOND_TS_IRQ_PIN), + .bus_num = 1, + .chip_select = 3, + .platform_data = &ccwmx51js_touch_data, + }, +}; + +void ccwmx51_init_2nd_touch(void) +{ + ccwmx51_2nd_touch_gpio_init(); + spi_register_board_info(ccwmx51_2nd_touch, ARRAY_SIZE(ccwmx51_2nd_touch)); +} +#else +void ccwmx51_init_2nd_touch(void) {} +#endif + static struct spi_board_info spi_devices[] = { #if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) - { /* SPIDEV */ - .modalias = "spidev", - .max_speed_hz = 6000000, - .bus_num = 1, - .chip_select = 1, + { /* SPIDEV */ + .modalias = "spidev", + .max_speed_hz = 6000000, + .bus_num = 1, + .chip_select = 1, }, - /* Add here other SPI devices, if any... */ #endif + /* Add here other SPI devices, if any... */ }; void ccwmx51_init_spidevices(void) @@ -234,7 +441,7 @@ struct mxc_w1_config mxc_w1_data = { #if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE) static struct resource smsc911x_device_resources[] = { - { + { .name = "smsc911x-memory", .start = CS5_BASE_ADDR, .end = CS5_BASE_ADDR + SZ_4K - 1, @@ -248,10 +455,10 @@ static struct resource smsc911x_device_resources[] = { }; struct platform_device smsc911x_device = { - .name = "smsc911x", - .id = -1, - .num_resources = ARRAY_SIZE(smsc911x_device_resources), - .resource = smsc911x_device_resources, + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(smsc911x_device_resources), + .resource = smsc911x_device_resources, }; /* WEIM registers */ @@ -261,9 +468,9 @@ struct platform_device smsc911x_device = { #define CSRCR2 0x0C #define CSWCR1 0x10 -static void __init ccwmx51_init_ext_eth_mac(void) +static void ccwmx51_init_ext_eth_mac(void) { - __iomem u32 *weim_vbaddr; + __iomem void *weim_vbaddr; weim_vbaddr = ioremap(WEIM_BASE_ADDR, SZ_4K); if (weim_vbaddr == 0) { @@ -276,11 +483,11 @@ static void __init ccwmx51_init_ext_eth_mac(void) * RWSC=50, RADVA=2, RADVN=6, OEA=0, OEN=0, RCSA=0, RCSN=0, APR=0 * WAL=0, WBED=1, WWSC=50, WADVA=2, WADVN=6, WEA=0, WEN=0, WCSA=0 */ - writel(0x00420081, (unsigned int)(weim_vbaddr) + 0x78 + CSGCR1); - writel(0, (unsigned int)(weim_vbaddr) + 0x78 + CSGCR2); - writel(0x32260000, (unsigned int)(weim_vbaddr) + 0x78 + CSRCR1); - writel(0, (unsigned int)(weim_vbaddr) + 0x78 + CSRCR2); - writel(0x72080f00, (unsigned int)(weim_vbaddr) + 0x78 + CSWCR1); + __raw_writel(0x00420081, (u32)weim_vbaddr + 0x78 + CSGCR1); + __raw_writel(0, (u32)weim_vbaddr + 0x78 + CSGCR2); + __raw_writel(0x32260000, (u32)weim_vbaddr + 0x78 + CSRCR1); + __raw_writel(0, (u32)weim_vbaddr + 0x78 + CSRCR2); + __raw_writel(0x72080f00, (u32)weim_vbaddr + 0x78 + CSWCR1); iounmap(weim_vbaddr); @@ -319,10 +526,6 @@ struct mxc_dvfs_platform_data dvfs_core_data = { .ccm_cdcr_reg_addr = MXC_CCM_CDCR, .ccm_cacrr_reg_addr = MXC_CCM_CACRR, .ccm_cdhipr_reg_addr = MXC_CCM_CDHIPR, - .dvfs_thrs_reg_addr = MXC_DVFSTHRS, - .dvfs_coun_reg_addr = MXC_DVFSCOUN, - .dvfs_emac_reg_addr = MXC_DVFSEMAC, - .dvfs_cntr_reg_addr = MXC_DVFSCNTR, .prediv_mask = 0x1F800, .prediv_offset = 11, .prediv_val = 3, @@ -364,90 +567,606 @@ struct platform_pwm_backlight_data mxc_pwm_backlight_data = { }; struct mxc_audio_platform_data wm8753_data = { - .ssi_num = 1, - .src_port = 2, - .ext_port = 3, - .sysclk = 12000000, + .ssi_num = 1, + .src_port = 2, + .ext_port = 3, + .sysclk = 0 /* Set on the fly */, }; -struct mxc_fb_platform_data mx51_fb_data[] = { - /*VGA*/ +struct mxc_fb_platform_data mx51_fb_data[2] = { + /* DISP0 */ + { + .interface_pix_fmt = VIDEO_PIX_FMT, + .mode_str = "1024x768M-16@60", /* Default */ + }, + /* DISP1 */ { - .interface_pix_fmt = IPU_PIX_FMT_RGB24, - .mode_str = "1024x768M-16@60", /* Default */ + .interface_pix_fmt = IPU_PIX_FMT_RGB666, + .mode_str = "800x480-16@60", /* Default */ } }; -#if defined(CONFIG_UIO_PDRV_GENIRQ) || defined(CONFIG_UIO_PDRV_GENIRQ_MODULE) -struct uio_info gpu2d_platform_data = { - .name = "imx_gpu2d", - .version = "1", - .irq = MXC_INT_GPU2_IRQ, - .open = gpu2d_open, - .release = gpu2d_release, - .mmap = gpu2d_mmap, -}; -#endif #if defined(CONFIG_FB_MXC_SYNC_PANEL) || defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE) -struct ccwmx51_lcd_pdata * plcd_platform_data; +struct ccwmx51_lcd_pdata plcd_platform_data[2]; -struct ccwmx51_lcd_pdata * ccwmx51_get_display(char *name) +#if defined(CONFIG_VIDEO_AD9389) || defined(CONFIG_VIDEO_AD9389_MODULE) +static u32 ccwmx51_get_max_video_pclk(void) +{ + /** + * TODO get this value from the clock subsystem. + * 133MHz seems to cause problems with the ext clk. + */ + return KHZ2PICOS(132000); +} +#endif + +#if defined(CONFIG_CCWMX51_DISP1) +static char *video2_options[FB_MAX] __read_mostly; +static int ofonly2 __read_mostly; + +int fb2_get_options(char *name, char **option) +{ + char *opt, *options = NULL; + int opt_len, retval = 0; + int name_len = strlen(name), i; + + if (name_len && ofonly2 && strncmp(name, "offb", 4)) + retval = 1; + + if (name_len && !retval) { + for (i = 0; i < FB_MAX; i++) { + if (video2_options[i] == NULL) + continue; + opt_len = strlen(video2_options[i]); + if (!opt_len) + continue; + opt = video2_options[i]; + if (!strncmp(name, opt, name_len) && + opt[name_len] == ':') + options = opt + name_len + 1; + } + } + if (options && !strncmp(options, "off", 3)) + retval = 1; + + if (option) + *option = options; + + return retval; +} + +static int __init video2_setup(char *options) +{ + int i, global = 0; + + if (!options || !*options) + global = 1; + + if (!global && !strncmp(options, "ofonly", 6)) { + ofonly2 = 1; + global = 1; + } + + if (!global && !strstr(options, "fb:")) { + fb_mode_option = options; + global = 1; + } + + if (!global) { + for (i = 0; i < FB_MAX; i++) { + if (video2_options[i] == NULL) { + video2_options[i] = options; + break; + } + } + } + + return 1; +} +__setup("video2=", video2_setup); +#endif /* defined(CONFIG_CCWMX51_DISP1) */ + +struct ccwmx51_lcd_pdata * ccwmx51_find_video_config(struct ccwmx51_lcd_pdata list[], + int len, + const char *name) { -#if defined(CONFIG_CCWMX51_LQ070Y3DG3B) || defined(CONFIG_CCWMX51_CUSTOM) int i; - for (i = 0; i < ARRAY_SIZE(lcd_display_list); i++) - if (!strncmp(lcd_display_list[i].fb_pdata.mode->name, - name, strlen(lcd_display_list[i].fb_pdata.mode->name))) - return &lcd_display_list[i]; + for (i = 0; i < len; i++) + if (!strncmp(list[i].fb_pdata.mode->name, + name, strlen(list[i].fb_pdata.mode->name))) + return &list[i]; + return NULL; +} + +static char *ccwmx51_get_video_cmdline_opt(int dispif, const char *str) +{ + char *options = NULL; + int ret = 1; + int len = strlen(str); + +#if defined(CONFIG_CCWMX51_DISP0) + if (dispif == 0) { + ret = fb_get_options("displayfb", &options); + } +#endif +#if defined(CONFIG_CCWMX51_DISP1) + if (dispif == 1) { + ret = fb2_get_options("displayfb", &options); + } #endif + if (ret || !options) + return NULL; + if (!len || !strncasecmp(options, str, len)) + return &options[len]; + return NULL; } -int __init ccwmx51_init_fb(void) +#if defined(CONFIG_VIDEO_AD9389) || defined(CONFIG_VIDEO_AD9389_MODULE) +static void fb_dump_mode(const char *str, const struct fb_videomode *vm) { - char *options = NULL, *p; + if (!(debug & AD9389_DBG)) + return; + if (vm == NULL) + return; - if (fb_get_options("displayfb", &options)) - pr_warning("no display information available in command line\n"); + printk(KERN_INFO "%s geometry %u %u %u\n", + str, vm->xres, vm->yres, vm->pixclock); + printk(KERN_INFO "%s timings %u %u %u %u %u %u %u\n", str, vm->pixclock, vm->left_margin, + vm->right_margin, vm->upper_margin, vm->lower_margin, vm->hsync_len, vm->vsync_len); + printk(KERN_INFO "%s flag %u sync %u vmode %u %s\n", str, vm->flag, vm->sync, vm->vmode, + vm->flag & FB_MODE_IS_FIRST ? "preferred" : ""); +} - if (!options) - return -ENODEV; +static void fb_dump_var(const char *str, struct fb_var_screeninfo *var) +{ + if (!(debug & AD9389_DBG)) + return; + if (var == NULL) + return; - if (!strncasecmp(options, "VGA", 3)) { - pr_info("VGA interface is primary\n"); + printk(KERN_INFO "%s geometry %u %u %u %u\n", + str, var->xres, var->yres, var->xres_virtual, var->yres_virtual); + printk(KERN_INFO "%s offset %u %u %u %u %u\n", + str, var->xoffset, var->yoffset, var->height, var->width, var->bits_per_pixel); + printk(KERN_INFO "%s timings %u %u %u %u %u %u %u\n", + str, var->pixclock, var->left_margin, var->right_margin, + var->upper_margin, var->lower_margin, var->hsync_len, var->vsync_len); + printk(KERN_INFO "%s accel_flags %u sync %u vmode %u\n", + str, var->accel_flags, var->sync, var->vmode); + printk(KERN_INFO "%d bpp\n", var->bits_per_pixel); +} + +enum hdmi_mode get_hdmi_mode(struct ad9389_dev *ad9389, struct fb_videomode **vm, char **str, unsigned int *vpclk, int *ext_clk) +{ + struct ad9389_pdata *pdata = ad9389->client->dev.platform_data; + struct ccwmx51_lcd_pdata *panel; + char *p, *temp; + + if ((p = ccwmx51_get_video_cmdline_opt(pdata->dispif, "HDMI")) != NULL) { + DBG(AD9389_DBG, "HDMI: %s config on DISP%d\n", p, pdata->dispif); /* Get the desired configuration provided by the bootloader */ - if (options[3] != '@') { - pr_info("Video resolution for VGA interface not provided, using default\n"); - /* TODO set default video here */ + if (vpclk != NULL ) { + *vpclk = 0; + /* Parse pclk, it was passed through cmdline */ + if ((temp = strstr(p, "pclk=")) != NULL) { + *vpclk = (unsigned int)simple_strtoul(temp + 5, NULL, 10); + if (*vpclk < ccwmx51_get_max_video_pclk()) + *vpclk = 0; + } + DBG(AD9389_DBG, "HDMI: using cmdline pclk %d\n", *vpclk); + } + if (ext_clk != NULL ) { + /* For single display, default is internal clk and can be overrided by cmdline */ +#if !defined(CONFIG_CCWMX51_DISP0) || !defined(CONFIG_CCWMX51_DISP1) + *ext_clk = 1; +#else + *ext_clk = 0; +#endif + /* Parse ext_clk, it was passed through cmdline */ + if ((temp = strstr(p, "int_clk")) != NULL) + *ext_clk = 0; + if ((temp = strstr(p, "ext_clk")) != NULL) + *ext_clk = 1; + DBG(AD9389_DBG, "HDMI: using %s\n", ext_clk ? "ext_clk" : "int_clk"); + } + if (*p++ != '@') { + pr_info("Video resolution for HDMI interface not provided, using auto\n"); + return MODE_AUTO; + } else if (!strncasecmp(p, "auto@", 5)) { + *str = p + 5; + if ((temp = strchr(*str, ',')) != NULL) + *temp = '\0'; + DBG(AD9389_DBG, "HDMI: auto string %s\n", *str); + return MODE_AUTO_STRING; + } else if (!strncasecmp(p, "auto", 4)) { + DBG(AD9389_DBG, "HDMI: auto\n"); + return MODE_AUTO; + } else if ((panel = ccwmx51_find_video_config(ad9389_panel_list, + ARRAY_SIZE(ad9389_panel_list), + p)) != NULL) { + *vm = panel->fb_pdata.mode; + memcpy(&mx51_fb_data[pdata->dispif], + &plcd_platform_data[pdata->dispif].fb_pdata, + sizeof(struct mxc_fb_platform_data)); + DBG(AD9389_DBG, "HDMI: forced mode\n"); + return MODE_FORCED; } else { - options = &options[4]; - if (((p = strsep (&options, "@")) != NULL) && *p) { - if (!strcmp(p, "640x480x16")) { - strcpy(mx51_fb_data[0].mode_str, "640x480M-16@60"); - } else if (!strcmp(p, "800x600x16")) { - strcpy(mx51_fb_data[0].mode_str, "800x600M-16@60"); - } else if (!strcmp(p, "1024x768x16")) { - strcpy(mx51_fb_data[0].mode_str, "1024x768M-16@60"); - } else if (!strcmp(p, "1280x1024x16")) { - strcpy(mx51_fb_data[0].mode_str, "1280x1024M-16@60"); - } else - pr_warning("Unsuported video resolution: %s, using default\n", p); + *str = p; + if ((temp = strchr(*str, ',')) != NULL) + *temp = '\0'; + DBG(AD9389_DBG, "HDMI: string %s\n", *str); + return MODE_STRING; + } + } + return MODE_UNKNOWN; +} + +#define AD9389_STR_LEN 30 +static void mxc_videomode_to_var(struct ad9389_dev *ad9389, struct fb_var_screeninfo *var) +{ + struct fb_info *info = ad9389->fbi; + const struct fb_videomode *fbvmode = NULL; + char *modestr = NULL, str[AD9389_STR_LEN]; + unsigned int tpclk; + int modeidx, ext_clk; + enum hdmi_mode mode; + + var->bits_per_pixel = CONFIG_CCWMX51_DEFAULT_VIDEO_BPP; /* Set default bpp */ + /* First, check if we have a predefined mode through the kernel command line */ + mode = get_hdmi_mode(ad9389, (struct fb_videomode **)&fbvmode, &modestr, &tpclk, &ext_clk); + if (mode == MODE_AUTO) { + /* auto, or no video mode provided */ + strncpy(str, "HDMI auto selected mode:", AD9389_STR_LEN - 1); + fbvmode = fb_find_best_mode(var, &info->modelist); + if (!fbvmode) { + fbvmode = fb_find_best_display(&info->monspecs, &info->modelist); + if (!fbvmode) { + printk(KERN_WARNING + "HDMI: unable to find a valid video mode/screen," + " try forcing a mode\n"); + /* Use default... */ + fbvmode = &ad9389_1024x768x24; + strncpy(str, "HDMI default mode:", AD9389_STR_LEN - 1); + } + } + } else if (mode == MODE_FORCED) { + /* Selected video mode through cmd line parameters provided */ + strncpy(str, "HDMI forced mode:", AD9389_STR_LEN - 1); + } else if ((mode == MODE_STRING || mode == MODE_AUTO_STRING) && modestr) { + DBG(AD9389_DBG, "HDMI mode string: %s\n", modestr); + modeidx = fb_find_mode(var, info, modestr, + info->monspecs.modedb, + info->monspecs.modedb_len, + NULL, var->bits_per_pixel); + if (!(modeidx == 1 || modeidx == 1)) { + DBG(AD9389_DBG, "HDMI: unable to find valid mode (%s)\n", modestr); + return; + } + strncpy(str, "HDMI string mode:", AD9389_STR_LEN - 1); + } + str[AD9389_STR_LEN - 1] = 0; + if ((mode == MODE_AUTO) || (mode == MODE_FORCED)) { + fb_dump_mode(str, fbvmode); + fb_videomode_to_var(var, fbvmode); + } + + if (ext_clk) + var->sync |= FB_SYNC_EXT; + + /* Check if clock must be readjusted */ + if (tpclk != 0) + var->pixclock = tpclk; + + fb_dump_var(str, var); +} + +/** + * This function parses the list of supported video modes (got from fb_edid_to_monspecs) and + * filters out not supported configurations + */ +static void mxcfb_vmode_to_modelist(struct fb_videomode *modedb, int num, + struct list_head *head, struct fb_var_screeninfo *var) +{ + int i, xres = 0, yres = 0, aspratio = 0; + + INIT_LIST_HEAD(head); + + /** + * Add the modes we got through the monitor specs, filtering out those + * unsupported configurations. + */ + for (i = 0; i < num; i++) { + struct list_head *pos, *n; + struct fb_modelist *modelist; + int remove, vmaspratio; + + remove = 0; + vmaspratio = -1; + + /* Use the preferred mode to compute the aspect ratio */ + if (modedb[i].flag & FB_MODE_IS_FIRST) { + DBG(AD9389_DBG, "PREFERRED: %ux%u%s%u pclk=%u\n", + modedb[i].xres, modedb[i].yres, + (modedb[i].vmode & FB_VMODE_INTERLACED ) ? "i@" : "@", + modedb[i].refresh, modedb[i].pixclock); + + aspratio = modedb[i].xres * 10 / modedb[i].yres; + DBG(AD9389_DBG, "Aspect Ratio: %d\n", aspratio); + } + + if (modedb[i].yres) + vmaspratio = modedb[i].xres * 10 / modedb[i].yres; + + if (vmaspratio != aspratio) { + DBG(AD9389_DBG, "REMOVED: %ux%u%s%u pclk=%u (aspect ratio)\n", + modedb[i].xres, modedb[i].yres, + (modedb[i].vmode & FB_VMODE_INTERLACED ) ? "i@" : "@", + modedb[i].refresh, modedb[i].pixclock); + continue; + } + + /* Interlaced not supported */ + if (modedb[i].vmode & FB_VMODE_INTERLACED) { + DBG(AD9389_DBG, "REMOVED: %ux%u%s%u pclk=%u (interlaced modes not supported)\n", + modedb[i].xres, modedb[i].yres, + (modedb[i].vmode & FB_VMODE_INTERLACED ) ? "i@" : "@", + modedb[i].refresh, modedb[i].pixclock); + continue; + } + + /* If clock exceeds the max pixel clock supported, remove that video mode */ + if ((modedb[i].pixclock * 115 / 100) < ccwmx51_get_max_video_pclk()) { + DBG(AD9389_DBG, "REMOVED: %ux%u%s%u pclk=%u (exceed %u limit)\n", + modedb[i].xres, modedb[i].yres, + (modedb[i].vmode & FB_VMODE_INTERLACED ) ? "i@" : "@", + modedb[i].refresh, modedb[i].pixclock, ccwmx51_get_max_video_pclk()); + continue; + } + + /* If over the pixel clock limix, but close enough, set the max pixel clock freq */ + if (modedb[i].pixclock < ccwmx51_get_max_video_pclk()) + modedb[i].pixclock = ccwmx51_get_max_video_pclk(); + + /** + * Adjust timing to IPU restrictions (better done here, to avoid ipu driver to + * incorrectly calculate settings based on our configuration). + */ + if (modedb[i].lower_margin < 2) { + /* This will not affect much, so we dont adjust the pixel clock */ + DBG(AD9389_DBG, "ADJUSTED: lower margin from %u to 2\n", + modedb[i].lower_margin); + modedb[i].lower_margin = 2; + } + + /** + * Remove duplicated modes, selecting the best modes accordingly to the + * platform video constraints. + */ + list_for_each_safe(pos, n, head) { + modelist = list_entry(pos, struct fb_modelist, list); + + if ((modelist->mode.xres == modedb[i].xres) && + (modelist->mode.yres == modedb[i].yres)) { + + if (modedb[i].pixclock == ccwmx51_get_max_video_pclk()) { + /* If current mode pixclk is set to max clock, do not + * add this mode and use the existing one. */ + remove = 1; + } else if ((modelist->mode.refresh == modedb[i].refresh) && + (modedb[i].flag & FB_MODE_IS_DETAILED)) { + DBG(AD9389_DBG, "REMOVED: %ux%u%s%u pclk=%u (duplicated)\n", + modelist->mode.xres, modelist->mode.yres, + (modelist->mode.vmode & FB_VMODE_INTERLACED ) ? "i@" : "@", + modelist->mode.refresh, modelist->mode.pixclock); + list_del(pos); + kfree(pos); + } else { + /* Do not add this mode, it is not a detailed timing */ + remove = 1; + } } } - } else { - if ((plcd_platform_data = ccwmx51_get_display(options)) != NULL) { - memcpy(&mx51_fb_data[0], &plcd_platform_data->fb_pdata, sizeof(struct mxc_fb_platform_data)); - plcd_platform_data->vif = 0; /* Select video interface 0 */ + + if (!remove) { + fb_add_videomode(&modedb[i], head); + DBG(AD9389_DBG, "ADDING: Video mode %ux%u%s%u pclk=%u, %s detailed\n", + modedb[i].xres, modedb[i].yres, + (modedb[i].vmode & FB_VMODE_INTERLACED ) ? "i@" : "@", + modedb[i].refresh, modedb[i].pixclock, + (modedb[i].flag & FB_MODE_IS_DETAILED) ? "" : "no"); + + if (modedb[i].xres > xres && modedb[i].yres > yres) { + xres = modedb[i].xres; + yres = modedb[i].yres; + } } } + + /* Update var->xres and var->yres, used to determine the best video mode*/ + if (var->xres != xres || var->yres != yres) { + var->xres = xres; + var->yres = yres; + } +} + +static int ccwmx51_hdmi_hw_init(struct ad9389_dev *ad9389) +{ + struct ad9389_pdata *pdata = ad9389->client->dev.platform_data; + + if (pdata->dispif == 0) { + mxc_request_iomux(AD9389_GPIO_IRQ, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION); + mxc_iomux_set_pad(AD9389_GPIO_IRQ, PAD_CTL_SRE_SLOW | PAD_CTL_DRV_MEDIUM | + PAD_CTL_100K_PU | PAD_CTL_HYS_ENABLE | + PAD_CTL_DRV_VOT_HIGH); + + gpio_request(IOMUX_TO_GPIO(AD9389_GPIO_IRQ), "ad9389_irq"); + gpio_direction_input(IOMUX_TO_GPIO(AD9389_GPIO_IRQ)); + + set_irq_type(IOMUX_TO_GPIO(AD9389_GPIO_IRQ), IRQ_TYPE_EDGE_BOTH); + } + + /* Configure here the hot plug detection for HDMI on DISP1 */ + /* if (pdata->dispif == 1) { } */ + + gpio_video_active(pdata->dispif, + PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST); + return 0; } -device_initcall(ccwmx51_init_fb); + +static void ccwmx51_hdmi_disp_connected(struct ad9389_dev *ad9389) +{ + printk(KERN_DEBUG "%s: display connected\n", __func__); +} + +static void ccwmx51_hdmi_disp_disconnected(struct ad9389_dev *ad9389) +{ + printk(KERN_DEBUG "%s: display disconnected\n", __func__); +} + +static struct ad9389_pdata hdmi_pdata = { + .hw_init = &ccwmx51_hdmi_hw_init, + .disp_connected = &ccwmx51_hdmi_disp_connected, + .disp_disconnected = &ccwmx51_hdmi_disp_disconnected, + .vmode_to_modelist = &mxcfb_vmode_to_modelist, + .vmode_to_var = &mxc_videomode_to_var, + .edid_addr = (0x7e >> 1), + .dispif = 0, +}; + +struct i2c_board_info ccwmx51_hdmi[] __initdata = { + { + I2C_BOARD_INFO("ad9389", 0x39), + .irq = IOMUX_TO_IRQ(AD9389_GPIO_IRQ), + .platform_data = &hdmi_pdata, + }, +}; +#endif + +#define MAX_VIDEO_IF 2 +int __init ccwmx51_init_fb(void) +{ + struct ccwmx51_lcd_pdata *panel; + char *p, *mstr; + int i; + + plcd_platform_data[0].vif = -1; + plcd_platform_data[1].vif = -1; + + for (i = 0; i < MAX_VIDEO_IF; i++) { +#if !defined(CONFIG_CCWMX51_DISP0) + if (i == 0) continue; +#endif +#if !defined(CONFIG_CCWMX51_DISP1) + if (i == 1) continue; +#endif + if ((p = ccwmx51_get_video_cmdline_opt(i, "HDMI")) != NULL) { +#if defined(CONFIG_VIDEO_AD9389) || defined(CONFIG_VIDEO_AD9389_MODULE) + pr_info("HDMI interface in DISP%d\n", i); + i2c_register_board_info(1, ccwmx51_hdmi, 1); +#else + pr_info("HDMI selected in DISP%d, but driver unavailable\n", i); + continue; +#endif + } else if ((p = ccwmx51_get_video_cmdline_opt(i, "LCD")) != NULL) { + pr_info("LCD interface in DISP%d", i); + if (*p++ != '@') { + pr_info("Panel not provided, video interface will be disabled\n"); + continue; + } + if ((panel = ccwmx51_find_video_config(lcd_panel_list, + ARRAY_SIZE(lcd_panel_list), + p)) != NULL) { + pr_info("Panel: %s", p); + memcpy(&plcd_platform_data[i], + panel, + sizeof(struct ccwmx51_lcd_pdata)); + memcpy(&mx51_fb_data[i], + &plcd_platform_data[i].fb_pdata, + sizeof(struct mxc_fb_platform_data)); + plcd_platform_data[i].vif = i; + mxc_register_device(&lcd_pdev[i], (void *)&plcd_platform_data[i]); + } + } else if ((p = ccwmx51_get_video_cmdline_opt(i, "VGA")) != NULL) { + pr_info("VGA interface in DISP%d\n", i); + gpio_video_active(i, PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST); + mstr = p - 3; + + /* Get the desired configuration provided by the bootloader */ + if (*p++ != '@') { + pr_info("Video resolution for VGA interface not provided, using default\n"); + } else { + /* Check string to see if its one of the configurations we alaredy have... + * and if not, pass it as mode string, just in case we want to use one + * of the standard video configurations + */ + if ((panel = ccwmx51_find_video_config(vga_panel_list, + ARRAY_SIZE(vga_panel_list), + p)) != NULL) { + pr_info("Panel: %s", p); + memcpy(&mx51_fb_data[i], + &plcd_platform_data[i].fb_pdata, + sizeof(struct mxc_fb_platform_data)); + } else { + /* Pass the video configuration as mode string */ + pr_info("VGA: string %s", p); + + if (!strcmp(p, "800x600")) { + strcpy(mx51_fb_data[0].mode_str, "VGA@800x600M-32"); + } else if (!strcmp(p, "1280x1024")) { + strcpy(mx51_fb_data[0].mode_str, "VGA@1280x1024M-32"); + } else { + strcpy(mx51_fb_data[0].mode_str, mstr); + } + } + } + } + mxc_fb_devices[i].num_resources = 1; + mxc_fb_devices[i].resource = &mxcfb_resources[i]; + mxc_register_device(&mxc_fb_devices[i], &mx51_fb_data[i]); + } + + /* DI0/1 DP-FG channel, used by the VPU */ + mxc_register_device(&mxc_fb_devices[2], NULL); + + return 0; +} +#endif + +#if defined(CONFIG_PATA_FSL) || defined(CONFIG_PATA_FSL_MODULE) +extern void gpio_ata_active(void); +extern void gpio_ata_inactive(void); + +static int ccwmx51_init_ata(struct platform_device *pdev) +{ + gpio_ata_active(); + return 0; +} + +static void ccwmx51_deinit_ata(void) +{ + gpio_ata_inactive(); +} + +struct fsl_ata_platform_data ata_data = { +#ifndef CONFIG_PATA_FSL_DISABLE_DMA + .udma_mask = ATA_UDMA3, + .mwdma_mask = ATA_MWDMA2, +#endif + .pio_mask = ATA_PIO4, + .fifo_alarm = MXC_IDE_DMA_WATERMARK / 2, + .max_sg = MXC_IDE_DMA_BD_NR, + .init = ccwmx51_init_ata, + .exit = ccwmx51_deinit_ata, + .core_reg = NULL, + .io_reg = NULL, +}; #endif -void __init ccwmx51_init_devices ( void ) +void ccwmx51_init_devices(void) { #if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE) ccwmx51_init_ext_eth_mac(); |