sunxi_dw_hdmi.c 9.7 KB

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