sunxi_dw_hdmi.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Allwinner DW HDMI bridge
  4. *
  5. * (C) Copyright 2017 Jernej Skrabec <jernej.skrabec@siol.net>
  6. */
  7. #include <common.h>
  8. #include <display.h>
  9. #include <dm.h>
  10. #include <dw_hdmi.h>
  11. #include <edid.h>
  12. #include <time.h>
  13. #include <asm/io.h>
  14. #include <asm/arch/clock.h>
  15. #include <asm/arch/lcdc.h>
  16. struct sunxi_dw_hdmi_priv {
  17. struct dw_hdmi hdmi;
  18. int mux;
  19. };
  20. struct sunxi_hdmi_phy {
  21. u32 pol;
  22. u32 res1[3];
  23. u32 read_en;
  24. u32 unscramble;
  25. u32 res2[2];
  26. u32 ctrl;
  27. u32 unk1;
  28. u32 unk2;
  29. u32 pll;
  30. u32 clk;
  31. u32 unk3;
  32. u32 status;
  33. };
  34. #define HDMI_PHY_OFFS 0x10000
  35. static int sunxi_dw_hdmi_get_divider(uint clock)
  36. {
  37. /*
  38. * Due to missing documentaion of HDMI PHY, we know correct
  39. * settings only for following four PHY dividers. Select one
  40. * based on clock speed.
  41. */
  42. if (clock <= 27000000)
  43. return 11;
  44. else if (clock <= 74250000)
  45. return 4;
  46. else if (clock <= 148500000)
  47. return 2;
  48. else
  49. return 1;
  50. }
  51. static void sunxi_dw_hdmi_phy_init(void)
  52. {
  53. struct sunxi_hdmi_phy * const phy =
  54. (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
  55. unsigned long tmo;
  56. u32 tmp;
  57. /*
  58. * HDMI PHY settings are taken as-is from Allwinner BSP code.
  59. * There is no documentation.
  60. */
  61. writel(0, &phy->ctrl);
  62. setbits_le32(&phy->ctrl, BIT(0));
  63. udelay(5);
  64. setbits_le32(&phy->ctrl, BIT(16));
  65. setbits_le32(&phy->ctrl, BIT(1));
  66. udelay(10);
  67. setbits_le32(&phy->ctrl, BIT(2));
  68. udelay(5);
  69. setbits_le32(&phy->ctrl, BIT(3));
  70. udelay(40);
  71. setbits_le32(&phy->ctrl, BIT(19));
  72. udelay(100);
  73. setbits_le32(&phy->ctrl, BIT(18));
  74. setbits_le32(&phy->ctrl, 7 << 4);
  75. /* Note that Allwinner code doesn't fail in case of timeout */
  76. tmo = timer_get_us() + 2000;
  77. while ((readl(&phy->status) & 0x80) == 0) {
  78. if (timer_get_us() > tmo) {
  79. printf("Warning: HDMI PHY init timeout!\n");
  80. break;
  81. }
  82. }
  83. setbits_le32(&phy->ctrl, 0xf << 8);
  84. setbits_le32(&phy->ctrl, BIT(7));
  85. writel(0x39dc5040, &phy->pll);
  86. writel(0x80084343, &phy->clk);
  87. udelay(10000);
  88. writel(1, &phy->unk3);
  89. setbits_le32(&phy->pll, BIT(25));
  90. udelay(100000);
  91. tmp = (readl(&phy->status) & 0x1f800) >> 11;
  92. setbits_le32(&phy->pll, BIT(31) | BIT(30));
  93. setbits_le32(&phy->pll, tmp);
  94. writel(0x01FF0F7F, &phy->ctrl);
  95. writel(0x80639000, &phy->unk1);
  96. writel(0x0F81C405, &phy->unk2);
  97. /* enable read access to HDMI controller */
  98. writel(0x54524545, &phy->read_en);
  99. /* descramble register offsets */
  100. writel(0x42494E47, &phy->unscramble);
  101. }
  102. static int sunxi_dw_hdmi_get_plug_in_status(void)
  103. {
  104. struct sunxi_hdmi_phy * const phy =
  105. (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
  106. return !!(readl(&phy->status) & (1 << 19));
  107. }
  108. static int sunxi_dw_hdmi_wait_for_hpd(void)
  109. {
  110. ulong start;
  111. start = get_timer(0);
  112. do {
  113. if (sunxi_dw_hdmi_get_plug_in_status())
  114. return 0;
  115. udelay(100);
  116. } while (get_timer(start) < 300);
  117. return -1;
  118. }
  119. static void sunxi_dw_hdmi_phy_set(uint clock, int phy_div)
  120. {
  121. struct sunxi_hdmi_phy * const phy =
  122. (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
  123. int div = sunxi_dw_hdmi_get_divider(clock);
  124. u32 tmp;
  125. /*
  126. * Unfortunately, we don't know much about those magic
  127. * numbers. They are taken from Allwinner BSP driver.
  128. */
  129. switch (div) {
  130. case 1:
  131. writel(0x30dc5fc0, &phy->pll);
  132. writel(0x800863C0 | (phy_div - 1), &phy->clk);
  133. mdelay(10);
  134. writel(0x00000001, &phy->unk3);
  135. setbits_le32(&phy->pll, BIT(25));
  136. mdelay(200);
  137. tmp = (readl(&phy->status) & 0x1f800) >> 11;
  138. setbits_le32(&phy->pll, BIT(31) | BIT(30));
  139. if (tmp < 0x3d)
  140. setbits_le32(&phy->pll, tmp + 2);
  141. else
  142. setbits_le32(&phy->pll, 0x3f);
  143. mdelay(100);
  144. writel(0x01FFFF7F, &phy->ctrl);
  145. writel(0x8063b000, &phy->unk1);
  146. writel(0x0F8246B5, &phy->unk2);
  147. break;
  148. case 2:
  149. writel(0x39dc5040, &phy->pll);
  150. writel(0x80084380 | (phy_div - 1), &phy->clk);
  151. mdelay(10);
  152. writel(0x00000001, &phy->unk3);
  153. setbits_le32(&phy->pll, BIT(25));
  154. mdelay(100);
  155. tmp = (readl(&phy->status) & 0x1f800) >> 11;
  156. setbits_le32(&phy->pll, BIT(31) | BIT(30));
  157. setbits_le32(&phy->pll, tmp);
  158. writel(0x01FFFF7F, &phy->ctrl);
  159. writel(0x8063a800, &phy->unk1);
  160. writel(0x0F81C485, &phy->unk2);
  161. break;
  162. case 4:
  163. writel(0x39dc5040, &phy->pll);
  164. writel(0x80084340 | (phy_div - 1), &phy->clk);
  165. mdelay(10);
  166. writel(0x00000001, &phy->unk3);
  167. setbits_le32(&phy->pll, BIT(25));
  168. mdelay(100);
  169. tmp = (readl(&phy->status) & 0x1f800) >> 11;
  170. setbits_le32(&phy->pll, BIT(31) | BIT(30));
  171. setbits_le32(&phy->pll, tmp);
  172. writel(0x01FFFF7F, &phy->ctrl);
  173. writel(0x8063b000, &phy->unk1);
  174. writel(0x0F81C405, &phy->unk2);
  175. break;
  176. case 11:
  177. writel(0x39dc5040, &phy->pll);
  178. writel(0x80084300 | (phy_div - 1), &phy->clk);
  179. mdelay(10);
  180. writel(0x00000001, &phy->unk3);
  181. setbits_le32(&phy->pll, BIT(25));
  182. mdelay(100);
  183. tmp = (readl(&phy->status) & 0x1f800) >> 11;
  184. setbits_le32(&phy->pll, BIT(31) | BIT(30));
  185. setbits_le32(&phy->pll, tmp);
  186. writel(0x01FFFF7F, &phy->ctrl);
  187. writel(0x8063b000, &phy->unk1);
  188. writel(0x0F81C405, &phy->unk2);
  189. break;
  190. }
  191. }
  192. static void sunxi_dw_hdmi_pll_set(uint clk_khz, int *phy_div)
  193. {
  194. int value, n, m, div, diff;
  195. int best_n = 0, best_m = 0, best_div = 0, best_diff = 0x0FFFFFFF;
  196. /*
  197. * Find the lowest divider resulting in a matching clock. If there
  198. * is no match, pick the closest lower clock, as monitors tend to
  199. * not sync to higher frequencies.
  200. */
  201. for (div = 1; div <= 16; div++) {
  202. int target = clk_khz * div;
  203. if (target < 192000)
  204. continue;
  205. if (target > 912000)
  206. continue;
  207. for (m = 1; m <= 16; m++) {
  208. n = (m * target) / 24000;
  209. if (n >= 1 && n <= 128) {
  210. value = (24000 * n) / m / div;
  211. diff = clk_khz - value;
  212. if (diff < best_diff) {
  213. best_diff = diff;
  214. best_m = m;
  215. best_n = n;
  216. best_div = div;
  217. }
  218. }
  219. }
  220. }
  221. *phy_div = best_div;
  222. clock_set_pll3_factors(best_m, best_n);
  223. debug("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n",
  224. clk_khz, (clock_get_pll3() / 1000) / best_div,
  225. best_n, best_m, best_div);
  226. }
  227. static void sunxi_dw_hdmi_lcdc_init(int mux, const struct display_timing *edid,
  228. int bpp)
  229. {
  230. struct sunxi_ccm_reg * const ccm =
  231. (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
  232. int div = DIV_ROUND_UP(clock_get_pll3(), edid->pixelclock.typ);
  233. struct sunxi_lcdc_reg *lcdc;
  234. if (mux == 0) {
  235. lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
  236. /* Reset off */
  237. setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
  238. /* Clock on */
  239. setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
  240. writel(CCM_LCD0_CTRL_GATE | CCM_LCD0_CTRL_M(div),
  241. &ccm->lcd0_clk_cfg);
  242. } else {
  243. lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD1_BASE;
  244. /* Reset off */
  245. setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD1);
  246. /* Clock on */
  247. setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD1);
  248. writel(CCM_LCD1_CTRL_GATE | CCM_LCD1_CTRL_M(div),
  249. &ccm->lcd1_clk_cfg);
  250. }
  251. lcdc_init(lcdc);
  252. lcdc_tcon1_mode_set(lcdc, edid, false, false);
  253. lcdc_enable(lcdc, bpp);
  254. }
  255. static int sunxi_dw_hdmi_phy_cfg(struct dw_hdmi *hdmi, uint mpixelclock)
  256. {
  257. int phy_div;
  258. sunxi_dw_hdmi_pll_set(mpixelclock / 1000, &phy_div);
  259. sunxi_dw_hdmi_phy_set(mpixelclock, phy_div);
  260. return 0;
  261. }
  262. static int sunxi_dw_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size)
  263. {
  264. struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
  265. return dw_hdmi_read_edid(&priv->hdmi, buf, buf_size);
  266. }
  267. static int sunxi_dw_hdmi_enable(struct udevice *dev, int panel_bpp,
  268. const struct display_timing *edid)
  269. {
  270. struct sunxi_hdmi_phy * const phy =
  271. (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
  272. struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
  273. int ret;
  274. ret = dw_hdmi_enable(&priv->hdmi, edid);
  275. if (ret)
  276. return ret;
  277. sunxi_dw_hdmi_lcdc_init(priv->mux, edid, panel_bpp);
  278. if (edid->flags & DISPLAY_FLAGS_VSYNC_LOW)
  279. setbits_le32(&phy->pol, 0x200);
  280. if (edid->flags & DISPLAY_FLAGS_HSYNC_LOW)
  281. setbits_le32(&phy->pol, 0x100);
  282. setbits_le32(&phy->ctrl, 0xf << 12);
  283. /*
  284. * This is last hdmi access before boot, so scramble addresses
  285. * again or othwerwise BSP driver won't work. Dummy read is
  286. * needed or otherwise last write doesn't get written correctly.
  287. */
  288. (void)readb(SUNXI_HDMI_BASE);
  289. writel(0, &phy->unscramble);
  290. return 0;
  291. }
  292. static int sunxi_dw_hdmi_probe(struct udevice *dev)
  293. {
  294. struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
  295. struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
  296. struct sunxi_ccm_reg * const ccm =
  297. (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
  298. int ret;
  299. /* Set pll3 to 297 MHz */
  300. clock_set_pll3(297000000);
  301. /* Set hdmi parent to pll3 */
  302. clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
  303. CCM_HDMI_CTRL_PLL3);
  304. /* Set ahb gating to pass */
  305. setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
  306. setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI2);
  307. setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
  308. setbits_le32(&ccm->hdmi_slow_clk_cfg, CCM_HDMI_SLOW_CTRL_DDC_GATE);
  309. /* Clock on */
  310. setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
  311. sunxi_dw_hdmi_phy_init();
  312. ret = sunxi_dw_hdmi_wait_for_hpd();
  313. if (ret < 0) {
  314. debug("hdmi can not get hpd signal\n");
  315. return -1;
  316. }
  317. priv->hdmi.ioaddr = SUNXI_HDMI_BASE;
  318. priv->hdmi.i2c_clk_high = 0xd8;
  319. priv->hdmi.i2c_clk_low = 0xfe;
  320. priv->hdmi.reg_io_width = 1;
  321. priv->hdmi.phy_set = sunxi_dw_hdmi_phy_cfg;
  322. priv->mux = uc_plat->source_id;
  323. uclass_get_device_by_phandle(UCLASS_I2C, dev, "ddc-i2c-bus",
  324. &priv->hdmi.ddc_bus);
  325. dw_hdmi_init(&priv->hdmi);
  326. return 0;
  327. }
  328. static const struct dm_display_ops sunxi_dw_hdmi_ops = {
  329. .read_edid = sunxi_dw_hdmi_read_edid,
  330. .enable = sunxi_dw_hdmi_enable,
  331. };
  332. U_BOOT_DRIVER(sunxi_dw_hdmi) = {
  333. .name = "sunxi_dw_hdmi",
  334. .id = UCLASS_DISPLAY,
  335. .ops = &sunxi_dw_hdmi_ops,
  336. .probe = sunxi_dw_hdmi_probe,
  337. .priv_auto_alloc_size = sizeof(struct sunxi_dw_hdmi_priv),
  338. };
  339. U_BOOT_DEVICE(sunxi_dw_hdmi) = {
  340. .name = "sunxi_dw_hdmi"
  341. };