sunxi_lcd.c 3.6 KB

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