Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/omapdrm: Workaround for errata i734 (LCD1 Gamma) in DSS dispc

Workaround for errata i734 in DSS dispc
- LCD1 Gamma Correction Is Not Working When GFX Pipe Is Disabled

For gamma tables to work on LCD1 the GFX plane has to be used at least
once after DSS HW has come out of reset. The workaround sets up a
minimal LCD setup with GFX plane and waits for one vertical sync irq
before disabling the setup and continuing with the context
restore. The physical outputs are gated during the operation.

For details see:
OMAP543x Multimedia Device Silicon Revision 2.0 Silicon Errata
Literature Number: SWPZ037E
Or some other relevant errata document for the DSS IP version.

Signed-off-by: Jyri Sarha <jsarha@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>

authored by

Jyri Sarha and committed by
Tomi Valkeinen
fbff010b acc3a231

+174
+174
drivers/gpu/drm/omapdrm/dss/dispc.c
··· 114 114 bool reverse_ilace_field_order:1; 115 115 116 116 bool has_gamma_table:1; 117 + 118 + bool has_gamma_i734_bug:1; 117 119 }; 118 120 119 121 #define DISPC_MAX_NR_FIFOS 5 ··· 4076 4074 .supports_double_pixel = true, 4077 4075 .reverse_ilace_field_order = true, 4078 4076 .has_gamma_table = true, 4077 + .has_gamma_i734_bug = true, 4079 4078 }; 4080 4079 4081 4080 static const struct dispc_features omap54xx_dispc_feats = { ··· 4103 4100 .supports_double_pixel = true, 4104 4101 .reverse_ilace_field_order = true, 4105 4102 .has_gamma_table = true, 4103 + .has_gamma_i734_bug = true, 4106 4104 }; 4107 4105 4108 4106 static int dispc_init_features(struct platform_device *pdev) ··· 4195 4191 } 4196 4192 EXPORT_SYMBOL(dispc_free_irq); 4197 4193 4194 + /* 4195 + * Workaround for errata i734 in DSS dispc 4196 + * - LCD1 Gamma Correction Is Not Working When GFX Pipe Is Disabled 4197 + * 4198 + * For gamma tables to work on LCD1 the GFX plane has to be used at 4199 + * least once after DSS HW has come out of reset. The workaround 4200 + * sets up a minimal LCD setup with GFX plane and waits for one 4201 + * vertical sync irq before disabling the setup and continuing with 4202 + * the context restore. The physical outputs are gated during the 4203 + * operation. This workaround requires that gamma table's LOADMODE 4204 + * is set to 0x2 in DISPC_CONTROL1 register. 4205 + * 4206 + * For details see: 4207 + * OMAP543x Multimedia Device Silicon Revision 2.0 Silicon Errata 4208 + * Literature Number: SWPZ037E 4209 + * Or some other relevant errata document for the DSS IP version. 4210 + */ 4211 + 4212 + static const struct dispc_errata_i734_data { 4213 + struct omap_video_timings timings; 4214 + struct omap_overlay_info ovli; 4215 + struct omap_overlay_manager_info mgri; 4216 + struct dss_lcd_mgr_config lcd_conf; 4217 + } i734 = { 4218 + .timings = { 4219 + .x_res = 8, .y_res = 1, 4220 + .pixelclock = 16000000, 4221 + .hsw = 8, .hfp = 4, .hbp = 4, 4222 + .vsw = 1, .vfp = 1, .vbp = 1, 4223 + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, 4224 + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, 4225 + .interlace = false, 4226 + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, 4227 + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, 4228 + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, 4229 + .double_pixel = false, 4230 + }, 4231 + .ovli = { 4232 + .screen_width = 1, 4233 + .width = 1, .height = 1, 4234 + .color_mode = OMAP_DSS_COLOR_RGB24U, 4235 + .rotation = OMAP_DSS_ROT_0, 4236 + .rotation_type = OMAP_DSS_ROT_DMA, 4237 + .mirror = 0, 4238 + .pos_x = 0, .pos_y = 0, 4239 + .out_width = 0, .out_height = 0, 4240 + .global_alpha = 0xff, 4241 + .pre_mult_alpha = 0, 4242 + .zorder = 0, 4243 + }, 4244 + .mgri = { 4245 + .default_color = 0, 4246 + .trans_enabled = false, 4247 + .partial_alpha_enabled = false, 4248 + .cpr_enable = false, 4249 + }, 4250 + .lcd_conf = { 4251 + .io_pad_mode = DSS_IO_PAD_MODE_BYPASS, 4252 + .stallmode = false, 4253 + .fifohandcheck = false, 4254 + .clock_info = { 4255 + .lck_div = 1, 4256 + .pck_div = 2, 4257 + }, 4258 + .video_port_width = 24, 4259 + .lcden_sig_polarity = 0, 4260 + }, 4261 + }; 4262 + 4263 + static struct i734_buf { 4264 + size_t size; 4265 + dma_addr_t paddr; 4266 + void *vaddr; 4267 + } i734_buf; 4268 + 4269 + static int dispc_errata_i734_wa_init(void) 4270 + { 4271 + if (!dispc.feat->has_gamma_i734_bug) 4272 + return 0; 4273 + 4274 + i734_buf.size = i734.ovli.width * i734.ovli.height * 4275 + color_mode_to_bpp(i734.ovli.color_mode) / 8; 4276 + 4277 + i734_buf.vaddr = dma_alloc_writecombine(&dispc.pdev->dev, i734_buf.size, 4278 + &i734_buf.paddr, GFP_KERNEL); 4279 + if (!i734_buf.vaddr) { 4280 + dev_err(&dispc.pdev->dev, "%s: dma_alloc_writecombine failed", 4281 + __func__); 4282 + return -ENOMEM; 4283 + } 4284 + 4285 + return 0; 4286 + } 4287 + 4288 + static void dispc_errata_i734_wa_fini(void) 4289 + { 4290 + if (!dispc.feat->has_gamma_i734_bug) 4291 + return; 4292 + 4293 + dma_free_writecombine(&dispc.pdev->dev, i734_buf.size, i734_buf.vaddr, 4294 + i734_buf.paddr); 4295 + } 4296 + 4297 + static void dispc_errata_i734_wa(void) 4298 + { 4299 + u32 framedone_irq = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_LCD); 4300 + struct omap_overlay_info ovli; 4301 + struct dss_lcd_mgr_config lcd_conf; 4302 + u32 gatestate; 4303 + unsigned int count; 4304 + 4305 + if (!dispc.feat->has_gamma_i734_bug) 4306 + return; 4307 + 4308 + gatestate = REG_GET(DISPC_CONFIG, 8, 4); 4309 + 4310 + ovli = i734.ovli; 4311 + ovli.paddr = i734_buf.paddr; 4312 + lcd_conf = i734.lcd_conf; 4313 + 4314 + /* Gate all LCD1 outputs */ 4315 + REG_FLD_MOD(DISPC_CONFIG, 0x1f, 8, 4); 4316 + 4317 + /* Setup and enable GFX plane */ 4318 + dispc_ovl_set_channel_out(OMAP_DSS_GFX, OMAP_DSS_CHANNEL_LCD); 4319 + dispc_ovl_setup(OMAP_DSS_GFX, &ovli, false, &i734.timings, false); 4320 + dispc_ovl_enable(OMAP_DSS_GFX, true); 4321 + 4322 + /* Set up and enable display manager for LCD1 */ 4323 + dispc_mgr_setup(OMAP_DSS_CHANNEL_LCD, &i734.mgri); 4324 + dispc_calc_clock_rates(dss_get_dispc_clk_rate(), 4325 + &lcd_conf.clock_info); 4326 + dispc_mgr_set_lcd_config(OMAP_DSS_CHANNEL_LCD, &lcd_conf); 4327 + dispc_mgr_set_timings(OMAP_DSS_CHANNEL_LCD, &i734.timings); 4328 + 4329 + dispc_clear_irqstatus(framedone_irq); 4330 + 4331 + /* Enable and shut the channel to produce just one frame */ 4332 + dispc_mgr_enable(OMAP_DSS_CHANNEL_LCD, true); 4333 + dispc_mgr_enable(OMAP_DSS_CHANNEL_LCD, false); 4334 + 4335 + /* Busy wait for framedone. We can't fiddle with irq handlers 4336 + * in PM resume. Typically the loop runs less than 5 times and 4337 + * waits less than a micro second. 4338 + */ 4339 + count = 0; 4340 + while (!(dispc_read_irqstatus() & framedone_irq)) { 4341 + if (count++ > 10000) { 4342 + dev_err(&dispc.pdev->dev, "%s: framedone timeout\n", 4343 + __func__); 4344 + break; 4345 + } 4346 + } 4347 + dispc_ovl_enable(OMAP_DSS_GFX, false); 4348 + 4349 + /* Clear all irq bits before continuing */ 4350 + dispc_clear_irqstatus(0xffffffff); 4351 + 4352 + /* Restore the original state to LCD1 output gates */ 4353 + REG_FLD_MOD(DISPC_CONFIG, gatestate, 8, 4); 4354 + } 4355 + 4198 4356 /* DISPC HW IP initialisation */ 4199 4357 static int dispc_bind(struct device *dev, struct device *master, void *data) 4200 4358 { ··· 4371 4205 spin_lock_init(&dispc.control_lock); 4372 4206 4373 4207 r = dispc_init_features(dispc.pdev); 4208 + if (r) 4209 + return r; 4210 + 4211 + r = dispc_errata_i734_wa_init(); 4374 4212 if (r) 4375 4213 return r; 4376 4214 ··· 4442 4272 void *data) 4443 4273 { 4444 4274 pm_runtime_disable(dev); 4275 + 4276 + dispc_errata_i734_wa_fini(); 4445 4277 } 4446 4278 4447 4279 static const struct component_ops dispc_component_ops = { ··· 4485 4313 */ 4486 4314 if (REG_GET(DISPC_CONFIG, 2, 1) != OMAP_DSS_LOAD_FRAME_ONLY) { 4487 4315 _omap_dispc_initial_config(); 4316 + 4317 + dispc_errata_i734_wa(); 4488 4318 4489 4319 dispc_restore_context(); 4490 4320