1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Freescale i.MX23/i.MX28 LCDIF driver 4 * 5 * Copyright (C) 2011-2013 Marek Vasut <marex@denx.de> 6 */ 7#include <clk.h> 8#include <dm.h> 9#include <env.h> 10#include <log.h> 11#include <asm/cache.h> 12#include <dm/device_compat.h> 13#include <linux/delay.h> 14#include <linux/errno.h> 15#include <malloc.h> 16#include <video.h> 17 18#include <asm/arch/clock.h> 19#include <asm/arch/imx-regs.h> 20#include <asm/arch/sys_proto.h> 21#include <asm/global_data.h> 22#include <asm/mach-imx/dma.h> 23#include <asm/io.h> 24 25#include "videomodes.h" 26 27#define PS2KHZ(ps) (1000000000UL / (ps)) 28#define HZ2PS(hz) (1000000000UL / ((hz) / 1000)) 29 30#define BITS_PP 18 31#define BYTES_PP 4 32 33struct mxs_dma_desc desc; 34 35/** 36 * mxsfb_system_setup() - Fine-tune LCDIF configuration 37 * 38 * This function is used to adjust the LCDIF configuration. This is usually 39 * needed when driving the controller in System-Mode to operate an 8080 or 40 * 6800 connected SmartLCD. 41 */ 42__weak void mxsfb_system_setup(void) 43{ 44} 45 46/* 47 * ARIES M28EVK: 48 * setenv videomode 49 * video=ctfb:x:800,y:480,depth:18,mode:0,pclk:30066, 50 * le:0,ri:256,up:0,lo:45,hs:1,vs:1,sync:100663296,vmode:0 51 * 52 * Freescale mx23evk/mx28evk with a Seiko 4.3'' WVGA panel: 53 * setenv videomode 54 * video=ctfb:x:800,y:480,depth:24,mode:0,pclk:29851, 55 * le:89,ri:164,up:23,lo:10,hs:10,vs:10,sync:0,vmode:0 56 */ 57 58static void mxs_lcd_init(struct udevice *dev, u32 fb_addr, 59 struct display_timing *timings, int bpp) 60{ 61 struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; 62 const enum display_flags flags = timings->flags; 63 uint32_t word_len = 0, bus_width = 0; 64 uint8_t valid_data = 0; 65 uint32_t vdctrl0; 66 67#if CONFIG_IS_ENABLED(CLK) 68 struct clk clk; 69 int ret; 70 71 ret = clk_get_by_name(dev, "pix", &clk); 72 if (ret) { 73 dev_err(dev, "Failed to get mxs pix clk: %d\n", ret); 74 return; 75 } 76 77 ret = clk_set_rate(&clk, timings->pixelclock.typ); 78 if (ret < 0) { 79 dev_err(dev, "Failed to set mxs pix clk: %d\n", ret); 80 return; 81 } 82 83 ret = clk_enable(&clk); 84 if (ret < 0) { 85 dev_err(dev, "Failed to enable mxs pix clk: %d\n", ret); 86 return; 87 } 88 89 ret = clk_get_by_name(dev, "axi", &clk); 90 if (ret < 0) { 91 debug("%s: Failed to get mxs axi clk: %d\n", __func__, ret); 92 } else { 93 ret = clk_enable(&clk); 94 if (ret < 0) { 95 dev_err(dev, "Failed to enable mxs axi clk: %d\n", ret); 96 return; 97 } 98 } 99 100 ret = clk_get_by_name(dev, "disp_axi", &clk); 101 if (ret < 0) { 102 debug("%s: Failed to get mxs disp_axi clk: %d\n", __func__, ret); 103 } else { 104 ret = clk_enable(&clk); 105 if (ret < 0) { 106 dev_err(dev, "Failed to enable mxs disp_axi clk: %d\n", ret); 107 return; 108 } 109 } 110#else 111 /* Kick in the LCDIF clock */ 112 mxs_set_lcdclk(MXS_LCDIF_BASE, timings->pixelclock.typ / 1000); 113#endif 114 115 /* Restart the LCDIF block */ 116 mxs_reset_block(®s->hw_lcdif_ctrl_reg); 117 118 switch (bpp) { 119 case 24: 120 word_len = LCDIF_CTRL_WORD_LENGTH_24BIT; 121 bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_24BIT; 122 valid_data = 0x7; 123 break; 124 case 18: 125 word_len = LCDIF_CTRL_WORD_LENGTH_24BIT; 126 bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_18BIT; 127 valid_data = 0x7; 128 break; 129 case 16: 130 word_len = LCDIF_CTRL_WORD_LENGTH_16BIT; 131 bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_16BIT; 132 valid_data = 0xf; 133 break; 134 case 8: 135 word_len = LCDIF_CTRL_WORD_LENGTH_8BIT; 136 bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_8BIT; 137 valid_data = 0xf; 138 break; 139 } 140 141 writel(bus_width | word_len | LCDIF_CTRL_DOTCLK_MODE | 142 LCDIF_CTRL_BYPASS_COUNT | LCDIF_CTRL_LCDIF_MASTER, 143 ®s->hw_lcdif_ctrl); 144 145 writel(valid_data << LCDIF_CTRL1_BYTE_PACKING_FORMAT_OFFSET, 146 ®s->hw_lcdif_ctrl1); 147 148 mxsfb_system_setup(); 149 150 writel((timings->vactive.typ << LCDIF_TRANSFER_COUNT_V_COUNT_OFFSET) | 151 timings->hactive.typ, ®s->hw_lcdif_transfer_count); 152 153 vdctrl0 = LCDIF_VDCTRL0_ENABLE_PRESENT | LCDIF_VDCTRL0_ENABLE_POL | 154 LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT | 155 LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT | 156 timings->vsync_len.typ; 157 158 if(flags & DISPLAY_FLAGS_HSYNC_HIGH) 159 vdctrl0 |= LCDIF_VDCTRL0_HSYNC_POL; 160 if(flags & DISPLAY_FLAGS_VSYNC_HIGH) 161 vdctrl0 |= LCDIF_VDCTRL0_VSYNC_POL; 162 if(flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) 163 vdctrl0 |= LCDIF_VDCTRL0_DOTCLK_POL; 164 if(flags & DISPLAY_FLAGS_DE_HIGH) 165 vdctrl0 |= LCDIF_VDCTRL0_ENABLE_POL; 166 167 writel(vdctrl0, ®s->hw_lcdif_vdctrl0); 168 writel(timings->vback_porch.typ + timings->vfront_porch.typ + 169 timings->vsync_len.typ + timings->vactive.typ, 170 ®s->hw_lcdif_vdctrl1); 171 writel((timings->hsync_len.typ << LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH_OFFSET) | 172 (timings->hback_porch.typ + timings->hfront_porch.typ + 173 timings->hsync_len.typ + timings->hactive.typ), 174 ®s->hw_lcdif_vdctrl2); 175 writel(((timings->hback_porch.typ + timings->hsync_len.typ) << 176 LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT_OFFSET) | 177 (timings->vback_porch.typ + timings->vsync_len.typ), 178 ®s->hw_lcdif_vdctrl3); 179 writel((0 << LCDIF_VDCTRL4_DOTCLK_DLY_SEL_OFFSET) | timings->hactive.typ, 180 ®s->hw_lcdif_vdctrl4); 181 182 writel(fb_addr, ®s->hw_lcdif_cur_buf); 183 writel(fb_addr, ®s->hw_lcdif_next_buf); 184 185 /* Flush FIFO first */ 186 writel(LCDIF_CTRL1_FIFO_CLEAR, ®s->hw_lcdif_ctrl1_set); 187 188#ifndef CONFIG_VIDEO_MXS_MODE_SYSTEM 189 /* Sync signals ON */ 190 setbits_le32(®s->hw_lcdif_vdctrl4, LCDIF_VDCTRL4_SYNC_SIGNALS_ON); 191#endif 192 193 /* FIFO cleared */ 194 writel(LCDIF_CTRL1_FIFO_CLEAR, ®s->hw_lcdif_ctrl1_clr); 195 196 /* RUN! */ 197 writel(LCDIF_CTRL_RUN, ®s->hw_lcdif_ctrl_set); 198} 199 200static int mxs_probe_common(struct udevice *dev, struct display_timing *timings, 201 int bpp, u32 fb) 202{ 203 /* Start framebuffer */ 204 mxs_lcd_init(dev, fb, timings, bpp); 205 206#ifdef CONFIG_VIDEO_MXS_MODE_SYSTEM 207 /* 208 * If the LCD runs in system mode, the LCD refresh has to be triggered 209 * manually by setting the RUN bit in HW_LCDIF_CTRL register. To avoid 210 * having to set this bit manually after every single change in the 211 * framebuffer memory, we set up specially crafted circular DMA, which 212 * sets the RUN bit, then waits until it gets cleared and repeats this 213 * infinitelly. This way, we get smooth continuous updates of the LCD. 214 */ 215 struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; 216 217 memset(&desc, 0, sizeof(struct mxs_dma_desc)); 218 desc.address = (dma_addr_t)&desc; 219 desc.cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | 220 MXS_DMA_DESC_WAIT4END | 221 (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); 222 desc.cmd.pio_words[0] = readl(®s->hw_lcdif_ctrl) | LCDIF_CTRL_RUN; 223 desc.cmd.next = (uint32_t)&desc.cmd; 224 225 /* Execute the DMA chain. */ 226 mxs_dma_circ_start(MXS_DMA_CHANNEL_AHB_APBH_LCDIF, &desc); 227#endif 228 229 return 0; 230} 231 232static int mxs_remove_common(u32 fb) 233{ 234 struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; 235 int timeout = 1000000; 236 237 if (!fb) 238 return -EINVAL; 239 240 writel(fb, ®s->hw_lcdif_cur_buf_reg); 241 writel(fb, ®s->hw_lcdif_next_buf_reg); 242 writel(LCDIF_CTRL1_VSYNC_EDGE_IRQ, ®s->hw_lcdif_ctrl1_clr); 243 while (--timeout) { 244 if (readl(®s->hw_lcdif_ctrl1_reg) & 245 LCDIF_CTRL1_VSYNC_EDGE_IRQ) 246 break; 247 udelay(1); 248 } 249 mxs_reset_block((struct mxs_register_32 *)®s->hw_lcdif_ctrl_reg); 250 251 return 0; 252} 253 254static int mxs_of_get_timings(struct udevice *dev, 255 struct display_timing *timings, 256 u32 *bpp) 257{ 258 int ret = 0; 259 u32 display_phandle; 260 ofnode display_node; 261 262 ret = ofnode_read_u32(dev_ofnode(dev), "display", &display_phandle); 263 if (ret) { 264 dev_err(dev, "required display property isn't provided\n"); 265 return -EINVAL; 266 } 267 268 display_node = ofnode_get_by_phandle(display_phandle); 269 if (!ofnode_valid(display_node)) { 270 dev_err(dev, "failed to find display subnode\n"); 271 return -EINVAL; 272 } 273 274 ret = ofnode_read_u32(display_node, "bits-per-pixel", bpp); 275 if (ret) { 276 dev_err(dev, 277 "required bits-per-pixel property isn't provided\n"); 278 return -EINVAL; 279 } 280 281 ret = ofnode_decode_display_timing(display_node, 0, timings); 282 if (ret) { 283 dev_err(dev, "failed to get any display timings\n"); 284 return -EINVAL; 285 } 286 287 return ret; 288} 289 290static int mxs_video_probe(struct udevice *dev) 291{ 292 struct video_uc_plat *plat = dev_get_uclass_plat(dev); 293 struct video_priv *uc_priv = dev_get_uclass_priv(dev); 294 295 struct display_timing timings; 296 u32 bpp = 0; 297 u32 fb_start, fb_end; 298 int ret; 299 300 debug("%s() plat: base 0x%lx, size 0x%x\n", 301 __func__, plat->base, plat->size); 302 303 ret = mxs_of_get_timings(dev, &timings, &bpp); 304 if (ret) 305 return ret; 306 307 ret = mxs_probe_common(dev, &timings, bpp, plat->base); 308 if (ret) 309 return ret; 310 311 switch (bpp) { 312 case 32: 313 case 24: 314 case 18: 315 uc_priv->bpix = VIDEO_BPP32; 316 break; 317 case 16: 318 uc_priv->bpix = VIDEO_BPP16; 319 break; 320 case 8: 321 uc_priv->bpix = VIDEO_BPP8; 322 break; 323 default: 324 dev_err(dev, "invalid bpp specified (bpp = %i)\n", bpp); 325 return -EINVAL; 326 } 327 328 uc_priv->xsize = timings.hactive.typ; 329 uc_priv->ysize = timings.vactive.typ; 330 331 /* Enable dcache for the frame buffer */ 332 fb_start = plat->base & ~(MMU_SECTION_SIZE - 1); 333 fb_end = plat->base + plat->size; 334 fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT); 335 mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start, 336 DCACHE_WRITEBACK); 337 video_set_flush_dcache(dev, true); 338 gd->fb_base = plat->base; 339 340 return ret; 341} 342 343static int mxs_video_bind(struct udevice *dev) 344{ 345 struct video_uc_plat *plat = dev_get_uclass_plat(dev); 346 struct display_timing timings; 347 u32 bpp = 0; 348 u32 bytes_pp = 0; 349 int ret; 350 351 ret = mxs_of_get_timings(dev, &timings, &bpp); 352 if (ret) 353 return ret; 354 355 switch (bpp) { 356 case 32: 357 case 24: 358 case 18: 359 bytes_pp = 4; 360 break; 361 case 16: 362 bytes_pp = 2; 363 break; 364 case 8: 365 bytes_pp = 1; 366 break; 367 default: 368 dev_err(dev, "invalid bpp specified (bpp = %i)\n", bpp); 369 return -EINVAL; 370 } 371 372 plat->size = timings.hactive.typ * timings.vactive.typ * bytes_pp; 373 374 return 0; 375} 376 377static int mxs_video_remove(struct udevice *dev) 378{ 379 struct video_uc_plat *plat = dev_get_uclass_plat(dev); 380 381 mxs_remove_common(plat->base); 382 383 return 0; 384} 385 386static const struct udevice_id mxs_video_ids[] = { 387 { .compatible = "fsl,imx23-lcdif" }, 388 { .compatible = "fsl,imx28-lcdif" }, 389 { .compatible = "fsl,imx6sx-lcdif" }, 390 { .compatible = "fsl,imx7ulp-lcdif" }, 391 { .compatible = "fsl,imxrt-lcdif" }, 392 { /* sentinel */ } 393}; 394 395U_BOOT_DRIVER(mxs_video) = { 396 .name = "mxs_video", 397 .id = UCLASS_VIDEO, 398 .of_match = mxs_video_ids, 399 .bind = mxs_video_bind, 400 .probe = mxs_video_probe, 401 .remove = mxs_video_remove, 402 .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE, 403}; 404