sunxi_lcd.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Allwinner LCD driver
  4. *
  5. * (C) Copyright 2017 Vasily Khoruzhick <anarsoul@gmail.com>
  6. */
  7. #include <common.h>
  8. #include <display.h>
  9. #include <video_bridge.h>
  10. #include <backlight.h>
  11. #include <dm.h>
  12. #include <edid.h>
  13. #include <asm/io.h>
  14. #include <asm/arch/clock.h>
  15. #include <asm/arch/lcdc.h>
  16. #include <asm/arch/gpio.h>
  17. #include <asm/gpio.h>
  18. struct sunxi_lcd_priv {
  19. struct display_timing timing;
  20. int panel_bpp;
  21. };
  22. static void sunxi_lcdc_config_pinmux(void)
  23. {
  24. #ifdef CONFIG_MACH_SUN50I
  25. int pin;
  26. for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(21); pin++) {
  27. sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0);
  28. sunxi_gpio_set_drv(pin, 3);
  29. }
  30. #endif
  31. }
  32. static int sunxi_lcd_enable(struct udevice *dev, int bpp,
  33. const struct display_timing *edid)
  34. {
  35. struct sunxi_ccm_reg * const ccm =
  36. (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
  37. struct sunxi_lcdc_reg * const lcdc =
  38. (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
  39. struct sunxi_lcd_priv *priv = dev_get_priv(dev);
  40. struct udevice *backlight;
  41. int clk_div, clk_double, ret;
  42. /* Reset off */
  43. setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
  44. /* Clock on */
  45. setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
  46. lcdc_init(lcdc);
  47. sunxi_lcdc_config_pinmux();
  48. lcdc_pll_set(ccm, 0, edid->pixelclock.typ / 1000,
  49. &clk_div, &clk_double, false);
  50. lcdc_tcon0_mode_set(lcdc, edid, clk_div, false,
  51. priv->panel_bpp, CONFIG_VIDEO_LCD_DCLK_PHASE);
  52. lcdc_enable(lcdc, priv->panel_bpp);
  53. ret = uclass_get_device(UCLASS_PANEL_BACKLIGHT, 0, &backlight);
  54. if (!ret)
  55. backlight_enable(backlight);
  56. return 0;
  57. }
  58. static int sunxi_lcd_read_timing(struct udevice *dev,
  59. struct display_timing *timing)
  60. {
  61. struct sunxi_lcd_priv *priv = dev_get_priv(dev);
  62. memcpy(timing, &priv->timing, sizeof(struct display_timing));
  63. return 0;
  64. }
  65. static int sunxi_lcd_probe(struct udevice *dev)
  66. {
  67. struct udevice *cdev;
  68. struct sunxi_lcd_priv *priv = dev_get_priv(dev);
  69. int ret;
  70. int node, timing_node, val;
  71. #ifdef CONFIG_VIDEO_BRIDGE
  72. /* Try to get timings from bridge first */
  73. ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &cdev);
  74. if (!ret) {
  75. u8 edid[EDID_SIZE];
  76. int channel_bpp;
  77. ret = video_bridge_attach(cdev);
  78. if (ret) {
  79. debug("video bridge attach failed: %d\n", ret);
  80. return ret;
  81. }
  82. ret = video_bridge_read_edid(cdev, edid, EDID_SIZE);
  83. if (ret > 0) {
  84. ret = edid_get_timing(edid, ret,
  85. &priv->timing, &channel_bpp);
  86. priv->panel_bpp = channel_bpp * 3;
  87. if (!ret)
  88. return ret;
  89. }
  90. }
  91. #endif
  92. /* Fallback to timings from DT if there's no bridge or
  93. * if reading EDID failed
  94. */
  95. ret = uclass_get_device(UCLASS_PANEL, 0, &cdev);
  96. if (ret) {
  97. debug("video panel not found: %d\n", ret);
  98. return ret;
  99. }
  100. if (fdtdec_decode_display_timing(gd->fdt_blob, dev_of_offset(cdev),
  101. 0, &priv->timing)) {
  102. debug("%s: Failed to decode display timing\n", __func__);
  103. return -EINVAL;
  104. }
  105. timing_node = fdt_subnode_offset(gd->fdt_blob, dev_of_offset(cdev),
  106. "display-timings");
  107. node = fdt_first_subnode(gd->fdt_blob, timing_node);
  108. val = fdtdec_get_int(gd->fdt_blob, node, "bits-per-pixel", -1);
  109. if (val != -1)
  110. priv->panel_bpp = val;
  111. else
  112. priv->panel_bpp = 18;
  113. return 0;
  114. }
  115. static const struct dm_display_ops sunxi_lcd_ops = {
  116. .read_timing = sunxi_lcd_read_timing,
  117. .enable = sunxi_lcd_enable,
  118. };
  119. U_BOOT_DRIVER(sunxi_lcd) = {
  120. .name = "sunxi_lcd",
  121. .id = UCLASS_DISPLAY,
  122. .ops = &sunxi_lcd_ops,
  123. .probe = sunxi_lcd_probe,
  124. .priv_auto_alloc_size = sizeof(struct sunxi_lcd_priv),
  125. };
  126. #ifdef CONFIG_MACH_SUN50I
  127. U_BOOT_DEVICE(sunxi_lcd) = {
  128. .name = "sunxi_lcd"
  129. };
  130. #endif