static int vga_parse_edid(void) { struct fb_monspecs *specs = NULL; if (ddev == NULL) { return -ENODEV; }else { specs = &ddev->specs; //free old edid if (ddev->edid) { kfree(ddev->edid); ddev->edid = NULL; } ddev->edid = kzalloc(EDID_LENGTH, GFP_KERNEL); if (!ddev->edid) return -ENOMEM; //read edid if (!vga_edid_read(ddev->edid, EDID_LENGTH)) { //free old fb_monspecs if(specs->modedb) kfree(specs->modedb); memset(specs, 0, sizeof(struct fb_monspecs)); //parse edid to fb_monspecs fb_edid_to_monspecs(ddev->edid, specs); }else { return -EIO; } } printk("vga-ddc: read and parse vga edid success.\n"); return 0; }
int hdmi_edid_parse_base(unsigned char *buf, int *extend_num, struct hdmi_edid *pedid) { int rc, i; if(buf == NULL || extend_num == NULL) return E_HDMI_EDID_PARAM; // #ifdef DEBUG // for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++) // { // hdmi_edid_debug("%02x ", buf[i]&0xff); // if((i+1) % 16 == 0) // hdmi_edid_debug("\n"); // } // #endif // Check first 8 byte to ensure it is an edid base block. if( buf[0] != 0x00 || buf[1] != 0xFF || buf[2] != 0xFF || buf[3] != 0xFF || buf[4] != 0xFF || buf[5] != 0xFF || buf[6] != 0xFF || buf[7] != 0x00) { hdmi_edid_error("[EDID] check header error\n"); return E_HDMI_EDID_HEAD; } *extend_num = buf[0x7e]; #ifdef DEBUG hdmi_edid_debug("[EDID] extend block num is %d\n", buf[0x7e]); #endif // Checksum rc = hdmi_edid_checksum(buf); if( rc != E_HDMI_EDID_SUCCESS) { hdmi_edid_error("[EDID] base block checksum error\n"); return E_HDMI_EDID_CHECKSUM; } pedid->specs = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL); if(pedid->specs == NULL) return E_HDMI_EDID_NOMEMORY; fb_edid_to_monspecs(buf, pedid->specs); return E_HDMI_EDID_SUCCESS; }
static ssize_t bmi_video_dvi_edid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmi_video *video = dev_get_drvdata(dev); struct fb_monspecs monspecs; /* Current Monitor specs */ if(video->dvi_monitor_edid) { ssize_t len = 0; fb_edid_to_monspecs( video->dvi_monitor_edid, &monspecs); len = fb_edid_show(&monspecs, buf, PAGE_SIZE, 5); fb_destroy_modedb(monspecs.modedb); return len; } return snprintf(buf,PAGE_SIZE,"(none)\n"); }
static int rk32_edp_read_edid(struct rk32_edp *edp) { unsigned char edid[EDID_LENGTH * 2]; unsigned int extend_block = 0; unsigned char sum; unsigned char test_vector; int retval; /* * EDID device address is 0x50. * However, if necessary, you must have set upper address * into E-EDID in I2C device, 0x30. */ /* Read Extension Flag, Number of 128-byte EDID extension blocks */ retval = rk32_edp_read_byte_from_i2c(edp, EDID_ADDR, EDID_EXTENSION_FLAG, &extend_block); if (retval < 0) { dev_err(edp->dev, "EDID extension flag failed!\n"); return -EIO; } if (extend_block > 0) { dev_dbg(edp->dev, "EDID data includes a single extension!\n"); /* Read EDID data */ retval = rk32_edp_read_bytes_from_i2c(edp, EDID_ADDR, EDID_HEADER, EDID_LENGTH, &edid[EDID_HEADER]); if (retval != 0) { dev_err(edp->dev, "EDID Read failed!\n"); return -EIO; } sum = edp_calc_edid_check_sum(edid); if (sum != 0) { dev_warn(edp->dev, "EDID bad checksum!\n"); return 0; } /* Read additional EDID data */ retval = rk32_edp_read_bytes_from_i2c(edp, EDID_ADDR, EDID_LENGTH, EDID_LENGTH, &edid[EDID_LENGTH]); if (retval != 0) { dev_err(edp->dev, "EDID Read failed!\n"); return -EIO; } sum = edp_calc_edid_check_sum(&edid[EDID_LENGTH]); if (sum != 0) { dev_warn(edp->dev, "EDID bad checksum!\n"); return 0; } retval = rk32_edp_read_byte_from_dpcd(edp, DPCD_TEST_REQUEST, &test_vector); if (retval < 0) { dev_err(edp->dev, "DPCD EDID Read failed!\n"); return retval; } if (test_vector & DPCD_TEST_EDID_READ) { retval = rk32_edp_write_byte_to_dpcd(edp, DPCD_TEST_EDID_CHECKSUM, edid[EDID_LENGTH + EDID_CHECKSUM]); if (retval < 0) { dev_err(edp->dev, "DPCD EDID Write failed!\n"); return retval; } retval = rk32_edp_write_byte_to_dpcd(edp, DPCD_TEST_RESPONSE, DPCD_TEST_EDID_CHECKSUM_WRITE); if (retval < 0) { dev_err(edp->dev, "DPCD EDID checksum failed!\n"); return retval; } } } else { dev_info(edp->dev, "EDID data does not include any extensions.\n"); /* Read EDID data */ retval = rk32_edp_read_bytes_from_i2c(edp, EDID_ADDR, EDID_HEADER, EDID_LENGTH, &edid[EDID_HEADER]); if (retval != 0) { dev_err(edp->dev, "EDID Read failed!\n"); return -EIO; } sum = edp_calc_edid_check_sum(edid); if (sum != 0) { dev_warn(edp->dev, "EDID bad checksum!\n"); return 0; } retval = rk32_edp_read_byte_from_dpcd(edp,DPCD_TEST_REQUEST, &test_vector); if (retval < 0) { dev_err(edp->dev, "DPCD EDID Read failed!\n"); return retval; } if (test_vector & DPCD_TEST_EDID_READ) { retval = rk32_edp_write_byte_to_dpcd(edp, DPCD_TEST_EDID_CHECKSUM, edid[EDID_CHECKSUM]); if (retval < 0) { dev_err(edp->dev, "DPCD EDID Write failed!\n"); return retval; } retval = rk32_edp_write_byte_to_dpcd(edp, DPCD_TEST_RESPONSE, DPCD_TEST_EDID_CHECKSUM_WRITE); if (retval < 0) { dev_err(edp->dev, "DPCD EDID checksum failed!\n"); return retval; } } } fb_edid_to_monspecs(edid, &edp->specs); dev_err(edp->dev, "EDID Read success!\n"); return 0; }
static void xylonfb_adv7511_get_monspecs(u8 *edid, struct fb_monspecs *monspecs, struct fb_var_screeninfo *var) { driver_devel("%s\n", __func__); fb_edid_to_monspecs(edid, monspecs); if (*(xfb_adv7511->xfb_flags) & XYLONFB_FLAG_EDID_PRINT) { pr_info("========================================\n"); pr_info("Display Information (EDID)\n"); pr_info("========================================\n"); pr_info("EDID Version %d.%d\n", (int)monspecs->version, (int)monspecs->revision); pr_info("Manufacturer: %s\n", monspecs->manufacturer); pr_info("Model: %x\n", monspecs->model); pr_info("Serial Number: %u\n", monspecs->serial); pr_info("Year: %u Week %u\n", monspecs->year, monspecs->week); pr_info("Display Characteristics:\n"); pr_info(" Monitor Operating Limits from EDID\n"); pr_info(" H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n", monspecs->hfmin/1000, monspecs->hfmax/1000, monspecs->vfmin, monspecs->vfmax, monspecs->dclkmax/1000000); if (monspecs->input & FB_DISP_DDI) { pr_info(" Digital Display Input\n"); } else { pr_info(" Analog Display Input:\n"); pr_info(" Input Voltage:\n"); if (monspecs->input & FB_DISP_ANA_700_300) pr_info(" 0.700V/0.300V"); else if (monspecs->input & FB_DISP_ANA_714_286) pr_info(" 0.714V/0.286V"); else if (monspecs->input & FB_DISP_ANA_1000_400) pr_info(" 1.000V/0.400V"); else if (monspecs->input & FB_DISP_ANA_700_000) pr_info(" 0.700V/0.000V"); } if (monspecs->signal) { pr_info(" Synchronization:\n"); if (monspecs->signal & FB_SIGNAL_BLANK_BLANK) pr_info(" Blank to Blank\n"); if (monspecs->signal & FB_SIGNAL_SEPARATE) pr_info(" Separate\n"); if (monspecs->signal & FB_SIGNAL_COMPOSITE) pr_info(" Composite\n"); if (monspecs->signal & FB_SIGNAL_SYNC_ON_GREEN) pr_info(" Sync on Green\n"); if (monspecs->signal & FB_SIGNAL_SERRATION_ON) pr_info(" Serration on\n"); } if (monspecs->max_x) pr_info(" Max H-size %dcm\n", monspecs->max_x); else pr_info(" Variable H-size\n"); if (monspecs->max_y) pr_info(" Max V-size %dcm\n", monspecs->max_y); else pr_info(" Variable V-size\n"); pr_info(" Display Gamma %d.%d\n", monspecs->gamma/100, monspecs->gamma % 100); pr_info(" DPMS: Active %s, Suspend %s, Standby %s\n", (monspecs->dpms & FB_DPMS_ACTIVE_OFF) ? "yes" : "no", (monspecs->dpms & FB_DPMS_SUSPEND) ? "yes" : "no", (monspecs->dpms & FB_DPMS_STANDBY) ? "yes" : "no"); if (monspecs->input & FB_DISP_MONO) pr_info(" Monochrome/Grayscale\n"); else if (monspecs->input & FB_DISP_RGB) pr_info(" RGB Color Display\n"); else if (monspecs->input & FB_DISP_MULTI) pr_info(" Non-RGB Multicolor Display\n"); else if (monspecs->input & FB_DISP_UNKNOWN) pr_info(" Unknown\n"); pr_info(" Chromaticity coordinates:\n"); pr_info(" RedX: 0.%03d\n", monspecs->chroma.redx); pr_info(" RedY: 0.%03d\n", monspecs->chroma.redy); pr_info(" GreenX: 0.%03d\n", monspecs->chroma.greenx); pr_info(" GreenY: 0.%03d\n", monspecs->chroma.greeny); pr_info(" BlueX: 0.%03d\n", monspecs->chroma.bluex); pr_info(" BlueY: 0.%03d\n", monspecs->chroma.bluey); pr_info(" WhiteX: 0.%03d\n", monspecs->chroma.whitex); pr_info(" WhiteY: 0.%03d\n", monspecs->chroma.whitey); if (monspecs->misc) { if (monspecs->misc & FB_MISC_PRIM_COLOR) pr_info(" Default color format is primary\n"); if (monspecs->misc & FB_MISC_1ST_DETAIL) pr_info(" First DETAILED Timing is preferred\n"); if (monspecs->gtf == 1) pr_info(" Display is GTF capable\n"); } pr_info("Monitor Timings\n"); pr_info(" Resolution %dx%d\n", var->xres, var->yres); pr_info(" Pixel Clock %d MHz ", (int)PICOS2KHZ(var->pixclock)/1000); pr_info(" H sync:\n"); pr_info(" Front porch %d Length %d Back porch %d\n", var->right_margin, var->hsync_len, var->left_margin); pr_info(" V sync:\n"); pr_info(" Front porch %d Length %d Back porch %d\n", var->lower_margin, var->vsync_len, var->upper_margin); pr_info(" %sHSync %sVSync\n", (var->sync & FB_SYNC_HOR_HIGH_ACT) ? "+" : "-", (var->sync & FB_SYNC_VERT_HIGH_ACT) ? "+" : "-"); pr_info("========================================\n"); } }
static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) { struct fb_var_screeninfo tmpvar; struct fb_var_screeninfo *var = &tmpvar; const struct fb_videomode *mode, *found = NULL; struct fb_info *info = hdmi->info; struct fb_modelist *modelist = NULL; unsigned int f_width = 0, f_height = 0, f_refresh = 0; unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ bool exact_match = false; u8 edid[128]; char *forced; int i; /* Read EDID */ dev_dbg(hdmi->dev, "Read back EDID code:"); for (i = 0; i < 128; i++) { edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); #ifdef DEBUG if ((i % 16) == 0) { printk(KERN_CONT "\n"); printk(KERN_DEBUG "%02X | %02X", i, edid[i]); } else { printk(KERN_CONT " %02X", edid[i]); } #endif } #ifdef DEBUG printk(KERN_CONT "\n"); #endif fb_edid_to_monspecs(edid, &hdmi->monspec); fb_get_options("sh_mobile_lcdc", &forced); if (forced && *forced) { /* Only primitive parsing so far */ i = sscanf(forced, "%ux%u@%u", &f_width, &f_height, &f_refresh); if (i < 2) { f_width = 0; f_height = 0; } dev_dbg(hdmi->dev, "Forced mode %ux%u@%uHz\n", f_width, f_height, f_refresh); } /* Walk monitor modes to find the best or the exact match */ for (i = 0, mode = hdmi->monspec.modedb; f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; i++, mode++) { unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode); /* No interest in unmatching modes */ if (f_width != mode->xres || f_height != mode->yres) continue; if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) /* * Exact match if either the refresh rate matches or it * hasn't been specified and we've found a mode, for * which we can configure the clock precisely */ exact_match = true; else if (found && found_rate_error <= rate_error) /* * We otherwise search for the closest matching clock * rate - either if no refresh rate has been specified * or we cannot find an exactly matching one */ continue; /* Check if supported: sufficient fb memory, supported clock-rate */ fb_videomode_to_var(var, mode); if (info && info->fbops->fb_check_var && info->fbops->fb_check_var(var, info)) { exact_match = false; continue; } found = mode; found_rate_error = rate_error; } /* * TODO 1: if no ->info is present, postpone running the config until * after ->info first gets registered. * TODO 2: consider registering the HDMI platform device from the LCDC * driver, and passing ->info with HDMI platform data. */ if (info && !found) { modelist = hdmi->info->modelist.next && !list_empty(&hdmi->info->modelist) ? list_entry(hdmi->info->modelist.next, struct fb_modelist, list) : NULL; if (modelist) { found = &modelist->mode; found_rate_error = sh_hdmi_rate_error(hdmi, found); } }
static int ldb_disp_init(struct mxc_dispdrv_handle *disp, struct mxc_dispdrv_setting *setting) { int ret = 0, i; struct ldb_data *ldb = mxc_dispdrv_getdata(disp); struct fsl_mxc_ldb_platform_data *plat_data = ldb->pdev->dev.platform_data; struct i2c_client *i2c_dev; struct resource *res; uint32_t base_addr; uint32_t reg, setting_idx; uint32_t ch_mask = 0, ch_val = 0; int lvds_channel = 0; uint32_t ipu_id, disp_id; /* if input format not valid, make RGB666 as default*/ if (!valid_mode(setting->if_fmt)) { dev_warn(&ldb->pdev->dev, "Input pixel format not valid" " use default RGB666\n"); setting->if_fmt = IPU_PIX_FMT_RGB666; } if (!ldb->inited) { char di_clk[] = "ipu1_di0_clk"; char ldb_clk[] = "ldb_di0_clk"; setting_idx = 0; res = platform_get_resource(ldb->pdev, IORESOURCE_MEM, 0); if (IS_ERR(res)) return -ENOMEM; base_addr = res->start; ldb->reg = ioremap(base_addr, res->end - res->start + 1); ldb->control_reg = ldb->reg + 2; ldb->gpr3_reg = ldb->reg + 3; ldb->lvds_bg_reg = regulator_get(&ldb->pdev->dev, plat_data->lvds_bg_reg); if (!IS_ERR(ldb->lvds_bg_reg)) { regulator_set_voltage(ldb->lvds_bg_reg, 2500000, 2500000); regulator_enable(ldb->lvds_bg_reg); } /* ipu selected by platform data setting */ setting->dev_id = plat_data->ipu_id; reg = readl(ldb->control_reg); /* refrence resistor select */ reg &= ~LDB_BGREF_RMODE_MASK; if (plat_data->ext_ref) reg |= LDB_BGREF_RMODE_EXT; else reg |= LDB_BGREF_RMODE_INT; /* TODO: now only use SPWG data mapping for both channel */ reg &= ~(LDB_BIT_MAP_CH0_MASK | LDB_BIT_MAP_CH1_MASK); reg |= LDB_BIT_MAP_CH0_SPWG | LDB_BIT_MAP_CH1_SPWG; /* channel mode setting */ reg &= ~(LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK); reg &= ~(LDB_DATA_WIDTH_CH0_MASK | LDB_DATA_WIDTH_CH1_MASK); if (bits_per_pixel(setting->if_fmt) == 24) reg |= LDB_DATA_WIDTH_CH0_24 | LDB_DATA_WIDTH_CH1_24; else reg |= LDB_DATA_WIDTH_CH0_18 | LDB_DATA_WIDTH_CH1_18; if (g_ldb_mode) ldb->mode = g_ldb_mode; else ldb->mode = plat_data->mode; if ((ldb->mode == LDB_SIN0) || (ldb->mode == LDB_SIN1)) { ret = ldb->mode - LDB_SIN0; if (plat_data->disp_id != ret) { dev_warn(&ldb->pdev->dev, "change IPU DI%d to IPU DI%d for LDB " "channel%d.\n", plat_data->disp_id, ret, ret); plat_data->disp_id = ret; } } else if (((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1)) && (cpu_is_mx6q() || cpu_is_mx6dl())) { if (plat_data->disp_id == plat_data->sec_disp_id) { dev_err(&ldb->pdev->dev, "For LVDS separate mode," "two DIs should be different!\n"); return -EINVAL; } if (((!plat_data->disp_id) && (ldb->mode == LDB_SEP1)) || ((plat_data->disp_id) && (ldb->mode == LDB_SEP0))) { dev_dbg(&ldb->pdev->dev, "LVDS separate mode:" "swap DI configuration!\n"); ipu_id = plat_data->ipu_id; disp_id = plat_data->disp_id; plat_data->ipu_id = plat_data->sec_ipu_id; plat_data->disp_id = plat_data->sec_disp_id; plat_data->sec_ipu_id = ipu_id; plat_data->sec_disp_id = disp_id; } } if (ldb->mode == LDB_SPL_DI0) { reg |= LDB_SPLIT_MODE_EN | LDB_CH0_MODE_EN_TO_DI0 | LDB_CH1_MODE_EN_TO_DI0; setting->disp_id = 0; } else if (ldb->mode == LDB_SPL_DI1) { reg |= LDB_SPLIT_MODE_EN | LDB_CH0_MODE_EN_TO_DI1 | LDB_CH1_MODE_EN_TO_DI1; setting->disp_id = 1; } else if (ldb->mode == LDB_DUL_DI0) { reg &= ~LDB_SPLIT_MODE_EN; reg |= LDB_CH0_MODE_EN_TO_DI0 | LDB_CH1_MODE_EN_TO_DI0; setting->disp_id = 0; } else if (ldb->mode == LDB_DUL_DI1) { reg &= ~LDB_SPLIT_MODE_EN; reg |= LDB_CH0_MODE_EN_TO_DI1 | LDB_CH1_MODE_EN_TO_DI1; setting->disp_id = 1; } else if (ldb->mode == LDB_SIN0) { reg &= ~LDB_SPLIT_MODE_EN; setting->disp_id = plat_data->disp_id; if (setting->disp_id == 0) reg |= LDB_CH0_MODE_EN_TO_DI0; else reg |= LDB_CH0_MODE_EN_TO_DI1; ch_mask = LDB_CH0_MODE_MASK; ch_val = reg & LDB_CH0_MODE_MASK; } else if (ldb->mode == LDB_SIN1) { reg &= ~LDB_SPLIT_MODE_EN; setting->disp_id = plat_data->disp_id; if (setting->disp_id == 0) reg |= LDB_CH1_MODE_EN_TO_DI0; else reg |= LDB_CH1_MODE_EN_TO_DI1; ch_mask = LDB_CH1_MODE_MASK; ch_val = reg & LDB_CH1_MODE_MASK; } else { /* separate mode*/ setting->disp_id = plat_data->disp_id; /* first output is LVDS0 or LVDS1 */ if (ldb->mode == LDB_SEP0) lvds_channel = 0; else lvds_channel = 1; reg &= ~LDB_SPLIT_MODE_EN; if ((lvds_channel == 0) && (setting->disp_id == 0)) reg |= LDB_CH0_MODE_EN_TO_DI0; else if ((lvds_channel == 0) && (setting->disp_id == 1)) reg |= LDB_CH0_MODE_EN_TO_DI1; else if ((lvds_channel == 1) && (setting->disp_id == 0)) reg |= LDB_CH1_MODE_EN_TO_DI0; else reg |= LDB_CH1_MODE_EN_TO_DI1; ch_mask = lvds_channel ? LDB_CH1_MODE_MASK : LDB_CH0_MODE_MASK; ch_val = reg & ch_mask; if (bits_per_pixel(setting->if_fmt) == 24) { if (lvds_channel == 0) reg &= ~LDB_DATA_WIDTH_CH1_24; else reg &= ~LDB_DATA_WIDTH_CH0_24; } else { if (lvds_channel == 0) reg &= ~LDB_DATA_WIDTH_CH1_18; else reg &= ~LDB_DATA_WIDTH_CH0_18; } } writel(reg, ldb->control_reg); if (ldb->mode < LDB_SIN0) { ch_mask = LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK; ch_val = reg & (LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK); } /* clock setting */ if ((cpu_is_mx6q() || cpu_is_mx6dl()) && ((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1))) ldb_clk[6] += lvds_channel; else ldb_clk[6] += setting->disp_id; ldb->setting[setting_idx].ldb_di_clk = clk_get(&ldb->pdev->dev, ldb_clk); if (IS_ERR(ldb->setting[setting_idx].ldb_di_clk)) { dev_err(&ldb->pdev->dev, "get ldb clk0 failed\n"); iounmap(ldb->reg); return PTR_ERR(ldb->setting[setting_idx].ldb_di_clk); } di_clk[3] += setting->dev_id; di_clk[7] += setting->disp_id; ldb->setting[setting_idx].di_clk = clk_get(&ldb->pdev->dev, di_clk); if (IS_ERR(ldb->setting[setting_idx].di_clk)) { dev_err(&ldb->pdev->dev, "get di clk0 failed\n"); iounmap(ldb->reg); return PTR_ERR(ldb->setting[setting_idx].di_clk); } dev_dbg(&ldb->pdev->dev, "ldb_clk to di clk: %s -> %s\n", ldb_clk, di_clk); /* fb notifier for clk setting */ ldb->nb.notifier_call = ldb_fb_event, ret = fb_register_client(&ldb->nb); if (ret < 0) { iounmap(ldb->reg); return ret; } ldb->inited = true; i2c_dev = ldb_i2c_client[lvds_channel]; } else { /* second time for separate mode */ char di_clk[] = "ipu1_di0_clk"; char ldb_clk[] = "ldb_di0_clk"; if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1) || (ldb->mode == LDB_DUL_DI0) || (ldb->mode == LDB_DUL_DI1) || (ldb->mode == LDB_SIN0) || (ldb->mode == LDB_SIN1)) { dev_err(&ldb->pdev->dev, "for second ldb disp" "ldb mode should in separate mode\n"); return -EINVAL; } setting_idx = 1; if (cpu_is_mx6q() || cpu_is_mx6dl()) { setting->dev_id = plat_data->sec_ipu_id; setting->disp_id = plat_data->sec_disp_id; } else { setting->dev_id = plat_data->ipu_id; setting->disp_id = !plat_data->disp_id; } if (setting->disp_id == ldb->setting[0].di) { dev_err(&ldb->pdev->dev, "Err: for second ldb disp in" "separate mode, DI should be different!\n"); return -EINVAL; } /* second output is LVDS0 or LVDS1 */ if (ldb->mode == LDB_SEP0) lvds_channel = 1; else lvds_channel = 0; reg = readl(ldb->control_reg); if ((lvds_channel == 0) && (setting->disp_id == 0)) reg |= LDB_CH0_MODE_EN_TO_DI0; else if ((lvds_channel == 0) && (setting->disp_id == 1)) reg |= LDB_CH0_MODE_EN_TO_DI1; else if ((lvds_channel == 1) && (setting->disp_id == 0)) reg |= LDB_CH1_MODE_EN_TO_DI0; else reg |= LDB_CH1_MODE_EN_TO_DI1; ch_mask = lvds_channel ? LDB_CH1_MODE_MASK : LDB_CH0_MODE_MASK; ch_val = reg & ch_mask; if (bits_per_pixel(setting->if_fmt) == 24) { if (lvds_channel == 0) reg |= LDB_DATA_WIDTH_CH0_24; else reg |= LDB_DATA_WIDTH_CH1_24; } else { if (lvds_channel == 0) reg |= LDB_DATA_WIDTH_CH0_18; else reg |= LDB_DATA_WIDTH_CH1_18; } writel(reg, ldb->control_reg); /* clock setting */ if (cpu_is_mx6q() || cpu_is_mx6dl()) ldb_clk[6] += lvds_channel; else ldb_clk[6] += setting->disp_id; ldb->setting[setting_idx].ldb_di_clk = clk_get(&ldb->pdev->dev, ldb_clk); if (IS_ERR(ldb->setting[setting_idx].ldb_di_clk)) { dev_err(&ldb->pdev->dev, "get ldb clk1 failed\n"); return PTR_ERR(ldb->setting[setting_idx].ldb_di_clk); } di_clk[3] += setting->dev_id; di_clk[7] += setting->disp_id; ldb->setting[setting_idx].di_clk = clk_get(&ldb->pdev->dev, di_clk); if (IS_ERR(ldb->setting[setting_idx].di_clk)) { dev_err(&ldb->pdev->dev, "get di clk1 failed\n"); return PTR_ERR(ldb->setting[setting_idx].di_clk); } i2c_dev = ldb_i2c_client[lvds_channel]; dev_dbg(&ldb->pdev->dev, "ldb_clk to di clk: %s -> %s\n", ldb_clk, di_clk); } if (i2c_dev) mxc_dispdrv_setdev(ldb->disp_ldb, &i2c_dev->dev); else mxc_dispdrv_setdev(ldb->disp_ldb, NULL); ldb->setting[setting_idx].ch_mask = ch_mask; ldb->setting[setting_idx].ch_val = ch_val; if (cpu_is_mx6q() || cpu_is_mx6dl()) ldb_ipu_ldb_route(setting->dev_id, setting->disp_id, ldb); /* * ldb_di0_clk -> ipux_di0_clk * ldb_di1_clk -> ipux_di1_clk */ clk_set_parent(ldb->setting[setting_idx].di_clk, ldb->setting[setting_idx].ldb_di_clk); /* must use spec video mode defined by driver */ ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str, ldb_modedb, ldb_modedb_sz, NULL, setting->default_bpp); if (ret != 1) fb_videomode_to_var(&setting->fbi->var, &ldb_modedb[0]); INIT_LIST_HEAD(&setting->fbi->modelist); for (i = 0; i < ldb_modedb_sz; i++) { struct fb_videomode m; fb_var_to_videomode(&m, &setting->fbi->var); if (fb_mode_is_equal(&m, &ldb_modedb[i])) { fb_add_videomode(&ldb_modedb[i], &setting->fbi->modelist); break; } } /* get screen size in edid */ if (i2c_dev) { ret = mxc_ldb_edidread(i2c_dev); if (ret > 0) { fb_edid_to_monspecs(&g_edid[lvds_channel][0], &setting->fbi->monspecs); /* centimeter to millimeter */ setting->fbi->var.width = setting->fbi->monspecs.max_x * 10; setting->fbi->var.height = setting->fbi->monspecs.max_y * 10; } else { /* ignore i2c access failure */ ret = 0; } } /* save current ldb setting for fb notifier */ ldb->setting[setting_idx].active = true; ldb->setting[setting_idx].ipu = setting->dev_id; ldb->setting[setting_idx].di = setting->disp_id; return ret; }