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;
}
Example #2
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;
}
Example #3
0
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");
}
Example #4
0
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;
}
Example #5
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");
	}
}
Example #6
0
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);
		}
	}
Example #7
0
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;
}