diff options
Diffstat (limited to 'drivers/gpu/drm/panel/panel-simple.c')
| -rw-r--r-- | drivers/gpu/drm/panel/panel-simple.c | 407 | 
1 files changed, 372 insertions, 35 deletions
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 5a93c4edf1e4..28fa6ba7b767 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -30,6 +30,7 @@  #include <linux/regulator/consumer.h>  #include <video/display_timing.h> +#include <video/of_display_timing.h>  #include <video/videomode.h>  #include <drm/drm_crtc.h> @@ -37,6 +38,22 @@  #include <drm/drm_mipi_dsi.h>  #include <drm/drm_panel.h> +/** + * @modes: Pointer to array of fixed modes appropriate for this panel.  If + *         only one mode then this can just be the address of this the mode. + *         NOTE: cannot be used with "timings" and also if this is specified + *         then you cannot override the mode in the device tree. + * @num_modes: Number of elements in modes array. + * @timings: Pointer to array of display timings.  NOTE: cannot be used with + *           "modes" and also these will be used to validate a device tree + *           override if one is present. + * @num_timings: Number of elements in timings array. + * @bpc: Bits per color. + * @size: Structure containing the physical size of this panel. + * @delay: Structure containing various delay values for this panel. + * @bus_format: See MEDIA_BUS_FMT_... defines. + * @bus_flags: See DRM_BUS_FLAG_... defines. + */  struct panel_desc {  	const struct drm_display_mode *modes;  	unsigned int num_modes; @@ -92,6 +109,8 @@ struct panel_simple {  	struct i2c_adapter *ddc;  	struct gpio_desc *enable_gpio; + +	struct drm_display_mode override_mode;  };  static inline struct panel_simple *to_panel_simple(struct drm_panel *panel) @@ -99,16 +118,13 @@ static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)  	return container_of(panel, struct panel_simple, base);  } -static int panel_simple_get_fixed_modes(struct panel_simple *panel) +static unsigned int panel_simple_get_timings_modes(struct panel_simple *panel)  {  	struct drm_connector *connector = panel->base.connector;  	struct drm_device *drm = panel->base.drm;  	struct drm_display_mode *mode;  	unsigned int i, num = 0; -	if (!panel->desc) -		return 0; -  	for (i = 0; i < panel->desc->num_timings; i++) {  		const struct display_timing *dt = &panel->desc->timings[i];  		struct videomode vm; @@ -132,6 +148,16 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)  		num++;  	} +	return num; +} + +static unsigned int panel_simple_get_display_modes(struct panel_simple *panel) +{ +	struct drm_connector *connector = panel->base.connector; +	struct drm_device *drm = panel->base.drm; +	struct drm_display_mode *mode; +	unsigned int i, num = 0; +  	for (i = 0; i < panel->desc->num_modes; i++) {  		const struct drm_display_mode *m = &panel->desc->modes[i]; @@ -153,6 +179,44 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)  		num++;  	} +	return num; +} + +static int panel_simple_get_non_edid_modes(struct panel_simple *panel) +{ +	struct drm_connector *connector = panel->base.connector; +	struct drm_device *drm = panel->base.drm; +	struct drm_display_mode *mode; +	bool has_override = panel->override_mode.type; +	unsigned int num = 0; + +	if (!panel->desc) +		return 0; + +	if (has_override) { +		mode = drm_mode_duplicate(drm, &panel->override_mode); +		if (mode) { +			drm_mode_probed_add(connector, mode); +			num = 1; +		} else { +			dev_err(drm->dev, "failed to add override mode\n"); +		} +	} + +	/* Only add timings if override was not there or failed to validate */ +	if (num == 0 && panel->desc->num_timings) +		num = panel_simple_get_timings_modes(panel); + +	/* +	 * Only add fixed modes if timings/override added no mode. +	 * +	 * We should only ever have either the display timings specified +	 * or a fixed mode. Anything else is rather bogus. +	 */ +	WARN_ON(panel->desc->num_timings && panel->desc->num_modes); +	if (num == 0) +		num = panel_simple_get_display_modes(panel); +  	connector->display_info.bpc = panel->desc->bpc;  	connector->display_info.width_mm = panel->desc->size.width;  	connector->display_info.height_mm = panel->desc->size.height; @@ -269,7 +333,7 @@ static int panel_simple_get_modes(struct drm_panel *panel)  	}  	/* add hard-coded panel modes */ -	num += panel_simple_get_fixed_modes(p); +	num += panel_simple_get_non_edid_modes(p);  	return num;  } @@ -300,10 +364,58 @@ static const struct drm_panel_funcs panel_simple_funcs = {  	.get_timings = panel_simple_get_timings,  }; +#define PANEL_SIMPLE_BOUNDS_CHECK(to_check, bounds, field) \ +	(to_check->field.typ >= bounds->field.min && \ +	 to_check->field.typ <= bounds->field.max) +static void panel_simple_parse_panel_timing_node(struct device *dev, +						 struct panel_simple *panel, +						 const struct display_timing *ot) +{ +	const struct panel_desc *desc = panel->desc; +	struct videomode vm; +	unsigned int i; + +	if (WARN_ON(desc->num_modes)) { +		dev_err(dev, "Reject override mode: panel has a fixed mode\n"); +		return; +	} +	if (WARN_ON(!desc->num_timings)) { +		dev_err(dev, "Reject override mode: no timings specified\n"); +		return; +	} + +	for (i = 0; i < panel->desc->num_timings; i++) { +		const struct display_timing *dt = &panel->desc->timings[i]; + +		if (!PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hactive) || +		    !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hfront_porch) || +		    !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hback_porch) || +		    !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hsync_len) || +		    !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vactive) || +		    !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vfront_porch) || +		    !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vback_porch) || +		    !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vsync_len)) +			continue; + +		if (ot->flags != dt->flags) +			continue; + +		videomode_from_timing(ot, &vm); +		drm_display_mode_from_videomode(&vm, &panel->override_mode); +		panel->override_mode.type |= DRM_MODE_TYPE_DRIVER | +					     DRM_MODE_TYPE_PREFERRED; +		break; +	} + +	if (WARN_ON(!panel->override_mode.type)) +		dev_err(dev, "Reject override mode: No display_timing found\n"); +} +  static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)  {  	struct device_node *backlight, *ddc;  	struct panel_simple *panel; +	struct display_timing dt;  	int err;  	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); @@ -349,6 +461,9 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)  		}  	} +	if (!of_get_display_timing(dev->of_node, "panel-timing", &dt)) +		panel_simple_parse_panel_timing_node(dev, panel, &dt); +  	drm_panel_init(&panel->base);  	panel->base.dev = dev;  	panel->base.funcs = &panel_simple_funcs; @@ -496,22 +611,21 @@ static const struct panel_desc auo_b101aw03 = {  	},  }; -static const struct drm_display_mode auo_b101ean01_mode = { -	.clock = 72500, -	.hdisplay = 1280, -	.hsync_start = 1280 + 119, -	.hsync_end = 1280 + 119 + 32, -	.htotal = 1280 + 119 + 32 + 21, -	.vdisplay = 800, -	.vsync_start = 800 + 4, -	.vsync_end = 800 + 4 + 20, -	.vtotal = 800 + 4 + 20 + 8, -	.vrefresh = 60, +static const struct display_timing auo_b101ean01_timing = { +	.pixelclock = { 65300000, 72500000, 75000000 }, +	.hactive = { 1280, 1280, 1280 }, +	.hfront_porch = { 18, 119, 119 }, +	.hback_porch = { 21, 21, 21 }, +	.hsync_len = { 32, 32, 32 }, +	.vactive = { 800, 800, 800 }, +	.vfront_porch = { 4, 4, 4 }, +	.vback_porch = { 8, 8, 8 }, +	.vsync_len = { 18, 20, 20 },  };  static const struct panel_desc auo_b101ean01 = { -	.modes = &auo_b101ean01_mode, -	.num_modes = 1, +	.timings = &auo_b101ean01_timing, +	.num_timings = 1,  	.bpc = 6,  	.size = {  		.width = 217, @@ -724,9 +838,9 @@ static const struct panel_desc auo_g133han01 = {  static const struct display_timing auo_g185han01_timings = {  	.pixelclock = { 120000000, 144000000, 175000000 },  	.hactive = { 1920, 1920, 1920 }, -	.hfront_porch = { 18, 60, 74 }, -	.hback_porch = { 12, 44, 54 }, -	.hsync_len = { 10, 24, 32 }, +	.hfront_porch = { 36, 120, 148 }, +	.hback_porch = { 24, 88, 108 }, +	.hsync_len = { 20, 48, 64 },  	.vactive = { 1080, 1080, 1080 },  	.vfront_porch = { 6, 10, 40 },  	.vback_porch = { 2, 5, 20 }, @@ -1335,6 +1449,31 @@ static const struct panel_desc giantplus_gpg482739qs5 = {  	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,  }; +static const struct display_timing giantplus_gpm940b0_timing = { +	.pixelclock = { 13500000, 27000000, 27500000 }, +	.hactive = { 320, 320, 320 }, +	.hfront_porch = { 14, 686, 718 }, +	.hback_porch = { 50, 70, 255 }, +	.hsync_len = { 1, 1, 1 }, +	.vactive = { 240, 240, 240 }, +	.vfront_porch = { 1, 1, 179 }, +	.vback_porch = { 1, 21, 31 }, +	.vsync_len = { 1, 1, 6 }, +	.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW, +}; + +static const struct panel_desc giantplus_gpm940b0 = { +	.timings = &giantplus_gpm940b0_timing, +	.num_timings = 1, +	.bpc = 8, +	.size = { +		.width = 60, +		.height = 45, +	}, +	.bus_format = MEDIA_BUS_FMT_RGB888_3X8, +	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE, +}; +  static const struct display_timing hannstar_hsd070pww1_timing = {  	.pixelclock = { 64300000, 71100000, 82000000 },  	.hactive = { 1280, 1280, 1280 }, @@ -1578,23 +1717,32 @@ static const struct panel_desc innolux_g121x1_l03 = {  	},  }; -static const struct drm_display_mode innolux_n116bge_mode = { -	.clock = 76420, -	.hdisplay = 1366, -	.hsync_start = 1366 + 136, -	.hsync_end = 1366 + 136 + 30, -	.htotal = 1366 + 136 + 30 + 60, -	.vdisplay = 768, -	.vsync_start = 768 + 8, -	.vsync_end = 768 + 8 + 12, -	.vtotal = 768 + 8 + 12 + 12, -	.vrefresh = 60, -	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, +/* + * Datasheet specifies that at 60 Hz refresh rate: + * - total horizontal time: { 1506, 1592, 1716 } + * - total vertical time: { 788, 800, 868 } + * + * ...but doesn't go into exactly how that should be split into a front + * porch, back porch, or sync length.  For now we'll leave a single setting + * here which allows a bit of tweaking of the pixel clock at the expense of + * refresh rate. + */ +static const struct display_timing innolux_n116bge_timing = { +	.pixelclock = { 72600000, 76420000, 80240000 }, +	.hactive = { 1366, 1366, 1366 }, +	.hfront_porch = { 136, 136, 136 }, +	.hback_porch = { 60, 60, 60 }, +	.hsync_len = { 30, 30, 30 }, +	.vactive = { 768, 768, 768 }, +	.vfront_porch = { 8, 8, 8 }, +	.vback_porch = { 12, 12, 12 }, +	.vsync_len = { 12, 12, 12 }, +	.flags = DISPLAY_FLAGS_VSYNC_LOW | DISPLAY_FLAGS_HSYNC_LOW,  };  static const struct panel_desc innolux_n116bge = { -	.modes = &innolux_n116bge_mode, -	.num_modes = 1, +	.timings = &innolux_n116bge_timing, +	.num_timings = 1,  	.bpc = 6,  	.size = {  		.width = 256, @@ -2157,6 +2305,33 @@ static const struct panel_desc ontat_yx700wv03 = {  	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,  }; +static const struct drm_display_mode ortustech_com37h3m_mode  = { +	.clock = 22153, +	.hdisplay = 480, +	.hsync_start = 480 + 8, +	.hsync_end = 480 + 8 + 10, +	.htotal = 480 + 8 + 10 + 10, +	.vdisplay = 640, +	.vsync_start = 640 + 4, +	.vsync_end = 640 + 4 + 3, +	.vtotal = 640 + 4 + 3 + 4, +	.vrefresh = 60, +	.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +}; + +static const struct panel_desc ortustech_com37h3m = { +	.modes = &ortustech_com37h3m_mode, +	.num_modes = 1, +	.bpc = 8, +	.size = { +		.width = 56,	/* 56.16mm */ +		.height = 75,	/* 74.88mm */ +	}, +	.bus_format = MEDIA_BUS_FMT_RGB888_1X24, +	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE | +		     DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE, +}; +  static const struct drm_display_mode ortustech_com43h4m85ulc_mode  = {  	.clock = 25000,  	.hdisplay = 480, @@ -2354,6 +2529,59 @@ static const struct panel_desc samsung_ltn140at29_301 = {  	},  }; +static const struct drm_display_mode sharp_ld_d5116z01b_mode = { +	.clock = 168480, +	.hdisplay = 1920, +	.hsync_start = 1920 + 48, +	.hsync_end = 1920 + 48 + 32, +	.htotal = 1920 + 48 + 32 + 80, +	.vdisplay = 1280, +	.vsync_start = 1280 + 3, +	.vsync_end = 1280 + 3 + 10, +	.vtotal = 1280 + 3 + 10 + 57, +	.vrefresh = 60, +	.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, +}; + +static const struct panel_desc sharp_ld_d5116z01b = { +	.modes = &sharp_ld_d5116z01b_mode, +	.num_modes = 1, +	.bpc = 8, +	.size = { +		.width = 260, +		.height = 120, +	}, +	.bus_format = MEDIA_BUS_FMT_RGB888_1X24, +	.bus_flags = DRM_BUS_FLAG_DATA_MSB_TO_LSB, +}; + +static const struct drm_display_mode sharp_lq070y3dg3b_mode = { +	.clock = 33260, +	.hdisplay = 800, +	.hsync_start = 800 + 64, +	.hsync_end = 800 + 64 + 128, +	.htotal = 800 + 64 + 128 + 64, +	.vdisplay = 480, +	.vsync_start = 480 + 8, +	.vsync_end = 480 + 8 + 2, +	.vtotal = 480 + 8 + 2 + 35, +	.vrefresh = 60, +	.flags = DISPLAY_FLAGS_PIXDATA_POSEDGE, +}; + +static const struct panel_desc sharp_lq070y3dg3b = { +	.modes = &sharp_lq070y3dg3b_mode, +	.num_modes = 1, +	.bpc = 8, +	.size = { +		.width = 152,	/* 152.4mm */ +		.height = 91,	/* 91.4mm */ +	}, +	.bus_format = MEDIA_BUS_FMT_RGB888_1X24, +	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE | +		     DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE, +}; +  static const struct drm_display_mode sharp_lq035q7db03_mode = {  	.clock = 5500,  	.hdisplay = 240, @@ -2454,6 +2682,33 @@ static const struct panel_desc sharp_lq150x1lg11 = {  	.bus_format = MEDIA_BUS_FMT_RGB565_1X16,  }; +static const struct display_timing sharp_ls020b1dd01d_timing = { +	.pixelclock = { 2000000, 4200000, 5000000 }, +	.hactive = { 240, 240, 240 }, +	.hfront_porch = { 66, 66, 66 }, +	.hback_porch = { 1, 1, 1 }, +	.hsync_len = { 1, 1, 1 }, +	.vactive = { 160, 160, 160 }, +	.vfront_porch = { 52, 52, 52 }, +	.vback_porch = { 6, 6, 6 }, +	.vsync_len = { 10, 10, 10 }, +	.flags = DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW, +}; + +static const struct panel_desc sharp_ls020b1dd01d = { +	.timings = &sharp_ls020b1dd01d_timing, +	.num_timings = 1, +	.bpc = 6, +	.size = { +		.width = 42, +		.height = 28, +	}, +	.bus_format = MEDIA_BUS_FMT_RGB565_1X16, +	.bus_flags = DRM_BUS_FLAG_DE_HIGH +		   | DRM_BUS_FLAG_PIXDATA_NEGEDGE +		   | DRM_BUS_FLAG_SHARP_SIGNALS, +}; +  static const struct drm_display_mode shelly_sca07010_bfn_lnn_mode = {  	.clock = 33300,  	.hdisplay = 800, @@ -2578,6 +2833,64 @@ static const struct panel_desc tianma_tm070rvhg71 = {  	.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,  }; +static const struct drm_display_mode ti_nspire_cx_lcd_mode[] = { +	{ +		.clock = 10000, +		.hdisplay = 320, +		.hsync_start = 320 + 50, +		.hsync_end = 320 + 50 + 6, +		.htotal = 320 + 50 + 6 + 38, +		.vdisplay = 240, +		.vsync_start = 240 + 3, +		.vsync_end = 240 + 3 + 1, +		.vtotal = 240 + 3 + 1 + 17, +		.vrefresh = 60, +		.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +	}, +}; + +static const struct panel_desc ti_nspire_cx_lcd_panel = { +	.modes = ti_nspire_cx_lcd_mode, +	.num_modes = 1, +	.bpc = 8, +	.size = { +		.width = 65, +		.height = 49, +	}, +	.bus_format = MEDIA_BUS_FMT_RGB888_1X24, +	.bus_flags = DRM_BUS_FLAG_PIXDATA_NEGEDGE, +}; + +static const struct drm_display_mode ti_nspire_classic_lcd_mode[] = { +	{ +		.clock = 10000, +		.hdisplay = 320, +		.hsync_start = 320 + 6, +		.hsync_end = 320 + 6 + 6, +		.htotal = 320 + 6 + 6 + 6, +		.vdisplay = 240, +		.vsync_start = 240 + 0, +		.vsync_end = 240 + 0 + 1, +		.vtotal = 240 + 0 + 1 + 0, +		.vrefresh = 60, +		.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, +	}, +}; + +static const struct panel_desc ti_nspire_classic_lcd_panel = { +	.modes = ti_nspire_classic_lcd_mode, +	.num_modes = 1, +	/* The grayscale panel has 8 bit for the color .. Y (black) */ +	.bpc = 8, +	.size = { +		.width = 71, +		.height = 53, +	}, +	/* This is the grayscale bus format */ +	.bus_format = MEDIA_BUS_FMT_Y8_1X8, +	.bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE, +}; +  static const struct drm_display_mode toshiba_lt089ac29000_mode = {  	.clock = 79500,  	.hdisplay = 1280, @@ -2883,6 +3196,9 @@ static const struct of_device_id platform_of_match[] = {  		.compatible = "giantplus,gpg482739qs5",  		.data = &giantplus_gpg482739qs5  	}, { +		.compatible = "giantplus,gpm940b0", +		.data = &giantplus_gpm940b0, +	}, {  		.compatible = "hannstar,hsd070pww1",  		.data = &hannstar_hsd070pww1,  	}, { @@ -2979,6 +3295,12 @@ static const struct of_device_id platform_of_match[] = {  		.compatible = "ontat,yx700wv03",  		.data = &ontat_yx700wv03,  	}, { +		.compatible = "ortustech,com37h3m05dtc", +		.data = &ortustech_com37h3m, +	}, { +		.compatible = "ortustech,com37h3m99dtc", +		.data = &ortustech_com37h3m, +	}, {  		.compatible = "ortustech,com43h4m85ulc",  		.data = &ortustech_com43h4m85ulc,  	}, { @@ -3003,9 +3325,15 @@ static const struct of_device_id platform_of_match[] = {  		.compatible = "samsung,ltn140at29-301",  		.data = &samsung_ltn140at29_301,  	}, { +		.compatible = "sharp,ld-d5116z01b", +		.data = &sharp_ld_d5116z01b, +	}, {  		.compatible = "sharp,lq035q7db03",  		.data = &sharp_lq035q7db03,  	}, { +		.compatible = "sharp,lq070y3dg3b", +		.data = &sharp_lq070y3dg3b, +	}, {  		.compatible = "sharp,lq101k1ly04",  		.data = &sharp_lq101k1ly04,  	}, { @@ -3015,6 +3343,9 @@ static const struct of_device_id platform_of_match[] = {  		.compatible = "sharp,lq150x1lg11",  		.data = &sharp_lq150x1lg11,  	}, { +		.compatible = "sharp,ls020b1dd01d", +		.data = &sharp_ls020b1dd01d, +	}, {  		.compatible = "shelly,sca07010-bfn-lnn",  		.data = &shelly_sca07010_bfn_lnn,  	}, { @@ -3030,6 +3361,12 @@ static const struct of_device_id platform_of_match[] = {  		.compatible = "tianma,tm070rvhg71",  		.data = &tianma_tm070rvhg71,  	}, { +		.compatible = "ti,nspire-cx-lcd-panel", +		.data = &ti_nspire_cx_lcd_panel, +	}, { +		.compatible = "ti,nspire-classic-lcd-panel", +		.data = &ti_nspire_classic_lcd_panel, +	}, {  		.compatible = "toshiba,lt089ac29000",  		.data = &toshiba_lt089ac29000,  	}, {  |