Jelajahi Sumber

Merge branch 'common/fbdev' of master.kernel.org:/pub/scm/linux/kernel/git/lethal/sh-2.6

Paul Mundt 14 tahun lalu
induk
melakukan
12ddf37444
3 mengubah file dengan 128 tambahan dan 86 penghapusan
  1. 105 59
      drivers/video/sh_mobile_hdmi.c
  2. 20 27
      drivers/video/sh_mobile_lcdcfb.c
  3. 3 0
      include/video/sh_mobile_hdmi.h

+ 105 - 59
drivers/video/sh_mobile_hdmi.c

@@ -209,7 +209,8 @@ enum hotplug_state {
 struct sh_hdmi {
 	void __iomem *base;
 	enum hotplug_state hp_state;	/* hot-plug status */
-	bool preprogrammed_mode;	/* use a pre-programmed VIC or the external mode */
+	u8 preprogrammed_vic;		/* use a pre-programmed VIC or
+					   the external mode */
 	struct clk *hdmi_clk;
 	struct device *dev;
 	struct fb_info *info;
@@ -342,7 +343,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
 	hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION);
 
 	/* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */
-	if (!hdmi->preprogrammed_mode)
+	if (!hdmi->preprogrammed_vic)
 		hdmi_write(hdmi, sync | 1 | (voffset << 4),
 			   HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS);
 }
@@ -466,7 +467,18 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi)
  */
 static void sh_hdmi_phy_config(struct sh_hdmi *hdmi)
 {
-	if (hdmi->var.yres > 480) {
+	if (hdmi->var.pixclock < 10000) {
+		/* for 1080p8bit 148MHz */
+		hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1);
+		hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2);
+		hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3);
+		hdmi_write(hdmi, 0x4c, HDMI_SLIPHDMIT_PARAM_SETTINGS_5);
+		hdmi_write(hdmi, 0x1e, HDMI_SLIPHDMIT_PARAM_SETTINGS_6);
+		hdmi_write(hdmi, 0x48, HDMI_SLIPHDMIT_PARAM_SETTINGS_7);
+		hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8);
+		hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9);
+		hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10);
+	} else if (hdmi->var.pixclock < 30000) {
 		/* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */
 		/*
 		 * [1:0]	Speed_A
@@ -565,13 +577,11 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi)
 	hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3);
 
 	/*
-	 * VIC = 1280 x 720p: ignored if external config is used
-	 * Send 2 for 720 x 480p, 16 for 1080p, ignored in external mode
+	 * VIC should be ignored if external config is used, so, we could just use 0,
+	 * but play safe and use a valid value in any case just in case
 	 */
-	if (hdmi->var.yres == 1080 && hdmi->var.xres == 1920)
-		vic = 16;
-	else if (hdmi->var.yres == 480 && hdmi->var.xres == 720)
-		vic = 2;
+	if (hdmi->preprogrammed_vic)
+		vic = hdmi->preprogrammed_vic;
 	else
 		vic = 4;
 	hdmi_write(hdmi, vic, HDMI_CTRL_PKT_BUF_ACCESS_PB4);
@@ -685,11 +695,21 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi)
 }
 
 static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi,
-					const struct fb_videomode *mode)
+		const struct fb_videomode *mode,
+		unsigned long *hdmi_rate, unsigned long *parent_rate)
 {
-	long target = PICOS2KHZ(mode->pixclock) * 1000,
-		rate = clk_round_rate(hdmi->hdmi_clk, target);
-	unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX;
+	unsigned long target = PICOS2KHZ(mode->pixclock) * 1000, rate_error;
+	struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+	*hdmi_rate = clk_round_rate(hdmi->hdmi_clk, target);
+	if ((long)*hdmi_rate < 0)
+		*hdmi_rate = clk_get_rate(hdmi->hdmi_clk);
+
+	rate_error = (long)*hdmi_rate > 0 ? abs(*hdmi_rate - target) : ULONG_MAX;
+	if (rate_error && pdata->clk_optimize_parent)
+		rate_error = pdata->clk_optimize_parent(target, hdmi_rate, parent_rate);
+	else if (clk_get_parent(hdmi->hdmi_clk))
+		*parent_rate = clk_get_rate(clk_get_parent(hdmi->hdmi_clk));
 
 	dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n",
 		mode->left_margin, mode->xres,
@@ -697,14 +717,15 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi,
 		mode->upper_margin, mode->yres,
 		mode->lower_margin, mode->vsync_len);
 
-	dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target,
-		 rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0,
-		 mode->refresh);
+	dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz, p=%luHz\n", target,
+		rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0,
+		mode->refresh, *parent_rate);
 
 	return rate_error;
 }
 
-static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
+static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
+			     unsigned long *parent_rate)
 {
 	struct fb_var_screeninfo tmpvar;
 	struct fb_var_screeninfo *var = &tmpvar;
@@ -754,11 +775,14 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
 	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);
+		unsigned long rate_error;
 
 		/* No interest in unmatching modes */
 		if (f_width != mode->xres || f_height != mode->yres)
 			continue;
+
+		rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate);
+
 		if (f_refresh == mode->refresh || (!f_refresh && !rate_error))
 			/*
 			 * Exact match if either the refresh rate matches or it
@@ -802,7 +826,7 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
 
 		if (modelist) {
 			found = &modelist->mode;
-			found_rate_error = sh_hdmi_rate_error(hdmi, found);
+			found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate);
 		}
 	}
 
@@ -810,16 +834,27 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
 	if (!found)
 		return -ENXIO;
 
-	dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n",
-		 modelist ? "default" : "EDID", found->xres, found->yres,
-		 found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error);
-
-	if ((found->xres == 720 && found->yres == 480) ||
-	    (found->xres == 1280 && found->yres == 720) ||
-	    (found->xres == 1920 && found->yres == 1080))
-		hdmi->preprogrammed_mode = true;
+	if (found->xres == 640 && found->yres == 480 && found->refresh == 60)
+		hdmi->preprogrammed_vic = 1;
+	else if (found->xres == 720 && found->yres == 480 && found->refresh == 60)
+		hdmi->preprogrammed_vic = 2;
+	else if (found->xres == 720 && found->yres == 576 && found->refresh == 50)
+		hdmi->preprogrammed_vic = 17;
+	else if (found->xres == 1280 && found->yres == 720 && found->refresh == 60)
+		hdmi->preprogrammed_vic = 4;
+	else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 24)
+		hdmi->preprogrammed_vic = 32;
+	else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 50)
+		hdmi->preprogrammed_vic = 31;
+	else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 60)
+		hdmi->preprogrammed_vic = 16;
 	else
-		hdmi->preprogrammed_mode = false;
+		hdmi->preprogrammed_vic = 0;
+
+	dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n",
+		modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external",
+		found->xres, found->yres, found->refresh,
+		PICOS2KHZ(found->pixclock) * 1000, found_rate_error);
 
 	fb_videomode_to_var(&hdmi->var, found);
 	sh_hdmi_external_video_param(hdmi);
@@ -972,39 +1007,37 @@ static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi)
 
 /**
  * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock
- * @hdmi:	driver context
- * @pixclock:	pixel clock period in picoseconds
- * return:	configured positive rate if successful
- *		0 if couldn't set the rate, but managed to enable the clock
- *		negative error, if couldn't enable the clock
+ * @hdmi:		driver context
+ * @hdmi_rate:		HDMI clock frequency in Hz
+ * @parent_rate:	if != 0 - set parent clock rate for optimal precision
+ * return:		configured positive rate if successful
+ *			0 if couldn't set the rate, but managed to enable the
+ *			clock, negative error, if couldn't enable the clock
  */
-static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock)
+static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate,
+				  unsigned long parent_rate)
 {
-	long rate;
 	int ret;
 
-	rate = PICOS2KHZ(pixclock) * 1000;
-	rate = clk_round_rate(hdmi->hdmi_clk, rate);
-	if (rate > 0) {
-		ret = clk_set_rate(hdmi->hdmi_clk, rate);
+	if (parent_rate && clk_get_parent(hdmi->hdmi_clk)) {
+		ret = clk_set_rate(clk_get_parent(hdmi->hdmi_clk), parent_rate);
 		if (ret < 0) {
-			dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret);
-			rate = 0;
+			dev_warn(hdmi->dev, "Cannot set parent rate %ld: %d\n", parent_rate, ret);
+			hdmi_rate = clk_round_rate(hdmi->hdmi_clk, hdmi_rate);
 		} else {
-			dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate);
+			dev_dbg(hdmi->dev, "HDMI set parent frequency %lu\n", parent_rate);
 		}
-	} else {
-		rate = 0;
-		dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate);
 	}
 
-	ret = clk_enable(hdmi->hdmi_clk);
+	ret = clk_set_rate(hdmi->hdmi_clk, hdmi_rate);
 	if (ret < 0) {
-		dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret);
-		return ret;
+		dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", hdmi_rate, ret);
+		hdmi_rate = 0;
+	} else {
+		dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", hdmi_rate);
 	}
 
-	return rate;
+	return hdmi_rate;
 }
 
 /* Hotplug interrupt occurred, read EDID */
@@ -1024,16 +1057,17 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
 	mutex_lock(&hdmi->mutex);
 
 	if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) {
+		unsigned long parent_rate = 0, hdmi_rate;
+
 		/* A device has been plugged in */
 		pm_runtime_get_sync(hdmi->dev);
 
-		ret = sh_hdmi_read_edid(hdmi);
+		ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate);
 		if (ret < 0)
 			goto out;
 
 		/* Reconfigure the clock */
-		clk_disable(hdmi->hdmi_clk);
-		ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock);
+		ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate);
 		if (ret < 0)
 			goto out;
 
@@ -1071,6 +1105,10 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
 		if (!hdmi->info)
 			goto out;
 
+		hdmi->monspec.modedb_len = 0;
+		fb_destroy_modedb(hdmi->monspec.modedb);
+		hdmi->monspec.modedb = NULL;
+
 		acquire_console_sem();
 
 		/* HDMI disconnect */
@@ -1078,7 +1116,6 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
 
 		release_console_sem();
 		pm_runtime_put(hdmi->dev);
-		fb_destroy_modedb(hdmi->monspec.modedb);
 	}
 
 out:
@@ -1163,13 +1200,22 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
 		goto egetclk;
 	}
 
-	/* Some arbitrary relaxed pixclock just to get things started */
-	rate = sh_hdmi_clk_configure(hdmi, 37037);
+	/* An arbitrary relaxed pixclock just to get things started: from standard 480p */
+	rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037));
+	if (rate > 0)
+		rate = sh_hdmi_clk_configure(hdmi, rate, 0);
+
 	if (rate < 0) {
 		ret = rate;
 		goto erate;
 	}
 
+	ret = clk_enable(hdmi->hdmi_clk);
+	if (ret < 0) {
+		dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret);
+		goto erate;
+	}
+
 	dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate);
 
 	if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) {
@@ -1187,10 +1233,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, hdmi);
 
-	/* Product and revision IDs are 0 in sh-mobile version */
-	dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n",
-		 hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID));
-
 	/* Set up LCDC callbacks */
 	board_cfg = &pdata->lcd_chan->board_cfg;
 	board_cfg->owner = THIS_MODULE;
@@ -1203,6 +1245,10 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
 	pm_runtime_enable(&pdev->dev);
 	pm_runtime_resume(&pdev->dev);
 
+	/* Product and revision IDs are 0 in sh-mobile version */
+	dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n",
+		 hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID));
+
 	ret = request_irq(irq, sh_hdmi_hotplug, 0,
 			  dev_name(&pdev->dev), hdmi);
 	if (ret < 0) {

+ 20 - 27
drivers/video/sh_mobile_lcdcfb.c

@@ -54,8 +54,8 @@ static int lcdc_shared_regs[] = {
 };
 #define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs)
 
-#define DEFAULT_XRES 1280
-#define DEFAULT_YRES 1024
+#define MAX_XRES 1920
+#define MAX_YRES 1080
 
 static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
 	[LDDCKPAT1R] = 0x400,
@@ -115,15 +115,16 @@ static const struct fb_videomode default_720p = {
 	.xres = 1280,
 	.yres = 720,
 
-	.left_margin = 200,
-	.right_margin = 88,
-	.hsync_len = 48,
+	.left_margin = 220,
+	.right_margin = 110,
+	.hsync_len = 40,
 
 	.upper_margin = 20,
 	.lower_margin = 5,
 	.vsync_len = 5,
 
 	.pixclock = 13468,
+	.refresh = 60,
 	.sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
 };
 
@@ -913,22 +914,12 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
 {
 	struct sh_mobile_lcdc_chan *ch = info->par;
 
-	if (var->xres < 160 || var->xres > 1920 ||
-	    var->yres < 120 || var->yres > 1080 ||
-	    var->left_margin < 32 || var->left_margin > 320 ||
-	    var->right_margin < 12 || var->right_margin > 240 ||
-	    var->upper_margin < 12 || var->upper_margin > 120 ||
-	    var->lower_margin < 1 || var->lower_margin > 64 ||
-	    var->hsync_len < 32 || var->hsync_len > 240 ||
-	    var->vsync_len < 2 || var->vsync_len > 64 ||
-	    var->pixclock < 6000 || var->pixclock > 40000 ||
+	if (var->xres > MAX_XRES || var->yres > MAX_YRES ||
 	    var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) {
-		dev_warn(info->dev, "Invalid info: %u %u %u %u %u %u %u %u %u!\n",
-			 var->xres, var->yres,
-			 var->left_margin, var->right_margin,
-			 var->upper_margin, var->lower_margin,
-			 var->hsync_len, var->vsync_len,
-			 var->pixclock);
+		dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n",
+			 var->left_margin, var->xres, var->right_margin, var->hsync_len,
+			 var->upper_margin, var->yres, var->lower_margin, var->vsync_len,
+			 PICOS2KHZ(var->pixclock));
 		return -EINVAL;
 	}
 	return 0;
@@ -1197,6 +1188,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
 		const struct fb_videomode *mode = cfg->lcd_cfg;
 		unsigned long max_size = 0;
 		int k;
+		int num_cfg;
 
 		ch->info = framebuffer_alloc(0, &pdev->dev);
 		if (!ch->info) {
@@ -1224,7 +1216,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
 		}
 
 		if (!mode)
-			max_size = DEFAULT_XRES * DEFAULT_YRES;
+			max_size = MAX_XRES * MAX_YRES;
 		else if (max_cfg)
 			dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n",
 				max_cfg->xres, max_cfg->yres);
@@ -1232,8 +1224,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
 		info->fix = sh_mobile_lcdc_fix;
 		info->fix.smem_len = max_size * (cfg->bpp / 8) * 2;
 
-		if (!mode)
+		if (!mode) {
 			mode = &default_720p;
+			num_cfg = 1;
+		} else {
+			num_cfg = ch->cfg.num_cfg;
+		}
+
+		fb_videomode_to_modelist(mode, num_cfg, &info->modelist);
 
 		fb_videomode_to_var(var, mode);
 		/* Default Y virtual resolution is 2x panel size */
@@ -1281,10 +1279,6 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
 
 	for (i = 0; i < j; i++) {
 		struct sh_mobile_lcdc_chan *ch = priv->ch + i;
-		const struct fb_videomode *mode = ch->cfg.lcd_cfg;
-
-		if (!mode)
-			mode = &default_720p;
 
 		info = ch->info;
 
@@ -1297,7 +1291,6 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
 			}
 		}
 
-		fb_videomode_to_modelist(mode, ch->cfg.num_cfg, &info->modelist);
 		error = register_framebuffer(info);
 		if (error < 0)
 			goto err1;

+ 3 - 0
include/video/sh_mobile_hdmi.h

@@ -13,6 +13,7 @@
 
 struct sh_mobile_lcdc_chan_cfg;
 struct device;
+struct clk;
 
 /*
  * flags format
@@ -33,6 +34,8 @@ struct sh_mobile_hdmi_info {
 	struct sh_mobile_lcdc_chan_cfg	*lcd_chan;
 	struct device			*lcd_dev;
 	unsigned int			 flags;
+	long (*clk_optimize_parent)(unsigned long target, unsigned long *best_freq,
+				    unsigned long *parent_freq);
 };
 
 #endif