meson_dw_hdmi.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2016 BayLibre, SAS
  4. * Author: Neil Armstrong <narmstrong@baylibre.com>
  5. * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
  6. */
  7. #include <linux/clk.h>
  8. #include <linux/component.h>
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/of_graph.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/regulator/consumer.h>
  15. #include <linux/reset.h>
  16. #include <drm/bridge/dw_hdmi.h>
  17. #include <drm/drm_atomic_helper.h>
  18. #include <drm/drm_bridge.h>
  19. #include <drm/drm_device.h>
  20. #include <drm/drm_edid.h>
  21. #include <drm/drm_probe_helper.h>
  22. #include <drm/drm_print.h>
  23. #include <linux/videodev2.h>
  24. #include "meson_drv.h"
  25. #include "meson_dw_hdmi.h"
  26. #include "meson_registers.h"
  27. #define DRIVER_NAME "meson-dw-hdmi"
  28. #define DRIVER_DESC "Amlogic Meson HDMI-TX DRM driver"
  29. /**
  30. * DOC: HDMI Output
  31. *
  32. * HDMI Output is composed of :
  33. *
  34. * - A Synopsys DesignWare HDMI Controller IP
  35. * - A TOP control block controlling the Clocks and PHY
  36. * - A custom HDMI PHY in order convert video to TMDS signal
  37. *
  38. * .. code::
  39. *
  40. * ___________________________________
  41. * | HDMI TOP |<= HPD
  42. * |___________________________________|
  43. * | | |
  44. * | Synopsys HDMI | HDMI PHY |=> TMDS
  45. * | Controller |________________|
  46. * |___________________________________|<=> DDC
  47. *
  48. *
  49. * The HDMI TOP block only supports HPD sensing.
  50. * The Synopsys HDMI Controller interrupt is routed
  51. * through the TOP Block interrupt.
  52. * Communication to the TOP Block and the Synopsys
  53. * HDMI Controller is done a pair of addr+read/write
  54. * registers.
  55. * The HDMI PHY is configured by registers in the
  56. * HHI register block.
  57. *
  58. * Pixel data arrives in 4:4:4 format from the VENC
  59. * block and the VPU HDMI mux selects either the ENCI
  60. * encoder for the 576i or 480i formats or the ENCP
  61. * encoder for all the other formats including
  62. * interlaced HD formats.
  63. * The VENC uses a DVI encoder on top of the ENCI
  64. * or ENCP encoders to generate DVI timings for the
  65. * HDMI controller.
  66. *
  67. * GXBB, GXL and GXM embeds the Synopsys DesignWare
  68. * HDMI TX IP version 2.01a with HDCP and I2C & S/PDIF
  69. * audio source interfaces.
  70. *
  71. * We handle the following features :
  72. *
  73. * - HPD Rise & Fall interrupt
  74. * - HDMI Controller Interrupt
  75. * - HDMI PHY Init for 480i to 1080p60
  76. * - VENC & HDMI Clock setup for 480i to 1080p60
  77. * - VENC Mode setup for 480i to 1080p60
  78. *
  79. * What is missing :
  80. *
  81. * - PHY, Clock and Mode setup for 2k && 4k modes
  82. * - SDDC Scrambling mode for HDMI 2.0a
  83. * - HDCP Setup
  84. * - CEC Management
  85. */
  86. /* TOP Block Communication Channel */
  87. #define HDMITX_TOP_ADDR_REG 0x0
  88. #define HDMITX_TOP_DATA_REG 0x4
  89. #define HDMITX_TOP_CTRL_REG 0x8
  90. #define HDMITX_TOP_G12A_OFFSET 0x8000
  91. /* Controller Communication Channel */
  92. #define HDMITX_DWC_ADDR_REG 0x10
  93. #define HDMITX_DWC_DATA_REG 0x14
  94. #define HDMITX_DWC_CTRL_REG 0x18
  95. /* HHI Registers */
  96. #define HHI_MEM_PD_REG0 0x100 /* 0x40 */
  97. #define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 */
  98. #define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 */
  99. #define HHI_HDMI_PHY_CNTL1 0x3a4 /* 0xe9 */
  100. #define PHY_CNTL1_INIT 0x03900000
  101. #define PHY_INVERT BIT(17)
  102. #define HHI_HDMI_PHY_CNTL2 0x3a8 /* 0xea */
  103. #define HHI_HDMI_PHY_CNTL3 0x3ac /* 0xeb */
  104. #define HHI_HDMI_PHY_CNTL4 0x3b0 /* 0xec */
  105. #define HHI_HDMI_PHY_CNTL5 0x3b4 /* 0xed */
  106. static DEFINE_SPINLOCK(reg_lock);
  107. enum meson_venc_source {
  108. MESON_VENC_SOURCE_NONE = 0,
  109. MESON_VENC_SOURCE_ENCI = 1,
  110. MESON_VENC_SOURCE_ENCP = 2,
  111. };
  112. struct meson_dw_hdmi;
  113. struct meson_dw_hdmi_data {
  114. unsigned int (*top_read)(struct meson_dw_hdmi *dw_hdmi,
  115. unsigned int addr);
  116. void (*top_write)(struct meson_dw_hdmi *dw_hdmi,
  117. unsigned int addr, unsigned int data);
  118. unsigned int (*dwc_read)(struct meson_dw_hdmi *dw_hdmi,
  119. unsigned int addr);
  120. void (*dwc_write)(struct meson_dw_hdmi *dw_hdmi,
  121. unsigned int addr, unsigned int data);
  122. u32 cntl0_init;
  123. u32 cntl1_init;
  124. };
  125. struct meson_dw_hdmi {
  126. struct dw_hdmi_plat_data dw_plat_data;
  127. struct meson_drm *priv;
  128. struct device *dev;
  129. void __iomem *hdmitx;
  130. const struct meson_dw_hdmi_data *data;
  131. struct reset_control *hdmitx_apb;
  132. struct reset_control *hdmitx_ctrl;
  133. struct reset_control *hdmitx_phy;
  134. u32 irq_stat;
  135. struct dw_hdmi *hdmi;
  136. struct drm_bridge *bridge;
  137. };
  138. static inline int dw_hdmi_is_compatible(struct meson_dw_hdmi *dw_hdmi,
  139. const char *compat)
  140. {
  141. return of_device_is_compatible(dw_hdmi->dev->of_node, compat);
  142. }
  143. /* PHY (via TOP bridge) and Controller dedicated register interface */
  144. static unsigned int dw_hdmi_top_read(struct meson_dw_hdmi *dw_hdmi,
  145. unsigned int addr)
  146. {
  147. unsigned long flags;
  148. unsigned int data;
  149. spin_lock_irqsave(&reg_lock, flags);
  150. /* ADDR must be written twice */
  151. writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
  152. writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
  153. /* Read needs a second DATA read */
  154. data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG);
  155. data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG);
  156. spin_unlock_irqrestore(&reg_lock, flags);
  157. return data;
  158. }
  159. static unsigned int dw_hdmi_g12a_top_read(struct meson_dw_hdmi *dw_hdmi,
  160. unsigned int addr)
  161. {
  162. return readl(dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2));
  163. }
  164. static inline void dw_hdmi_top_write(struct meson_dw_hdmi *dw_hdmi,
  165. unsigned int addr, unsigned int data)
  166. {
  167. unsigned long flags;
  168. spin_lock_irqsave(&reg_lock, flags);
  169. /* ADDR must be written twice */
  170. writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
  171. writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
  172. /* Write needs single DATA write */
  173. writel(data, dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG);
  174. spin_unlock_irqrestore(&reg_lock, flags);
  175. }
  176. static inline void dw_hdmi_g12a_top_write(struct meson_dw_hdmi *dw_hdmi,
  177. unsigned int addr, unsigned int data)
  178. {
  179. writel(data, dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2));
  180. }
  181. /* Helper to change specific bits in PHY registers */
  182. static inline void dw_hdmi_top_write_bits(struct meson_dw_hdmi *dw_hdmi,
  183. unsigned int addr,
  184. unsigned int mask,
  185. unsigned int val)
  186. {
  187. unsigned int data = dw_hdmi->data->top_read(dw_hdmi, addr);
  188. data &= ~mask;
  189. data |= val;
  190. dw_hdmi->data->top_write(dw_hdmi, addr, data);
  191. }
  192. static unsigned int dw_hdmi_dwc_read(struct meson_dw_hdmi *dw_hdmi,
  193. unsigned int addr)
  194. {
  195. unsigned long flags;
  196. unsigned int data;
  197. spin_lock_irqsave(&reg_lock, flags);
  198. /* ADDR must be written twice */
  199. writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
  200. writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
  201. /* Read needs a second DATA read */
  202. data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG);
  203. data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG);
  204. spin_unlock_irqrestore(&reg_lock, flags);
  205. return data;
  206. }
  207. static unsigned int dw_hdmi_g12a_dwc_read(struct meson_dw_hdmi *dw_hdmi,
  208. unsigned int addr)
  209. {
  210. return readb(dw_hdmi->hdmitx + addr);
  211. }
  212. static inline void dw_hdmi_dwc_write(struct meson_dw_hdmi *dw_hdmi,
  213. unsigned int addr, unsigned int data)
  214. {
  215. unsigned long flags;
  216. spin_lock_irqsave(&reg_lock, flags);
  217. /* ADDR must be written twice */
  218. writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
  219. writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
  220. /* Write needs single DATA write */
  221. writel(data, dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG);
  222. spin_unlock_irqrestore(&reg_lock, flags);
  223. }
  224. static inline void dw_hdmi_g12a_dwc_write(struct meson_dw_hdmi *dw_hdmi,
  225. unsigned int addr, unsigned int data)
  226. {
  227. writeb(data, dw_hdmi->hdmitx + addr);
  228. }
  229. /* Bridge */
  230. /* Setup PHY bandwidth modes */
  231. static void meson_hdmi_phy_setup_mode(struct meson_dw_hdmi *dw_hdmi,
  232. const struct drm_display_mode *mode,
  233. bool mode_is_420)
  234. {
  235. struct meson_drm *priv = dw_hdmi->priv;
  236. unsigned int pixel_clock = mode->clock;
  237. /* For 420, pixel clock is half unlike venc clock */
  238. if (mode_is_420) pixel_clock /= 2;
  239. if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
  240. dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi")) {
  241. if (pixel_clock >= 371250) {
  242. /* 5.94Gbps, 3.7125Gbps */
  243. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x333d3282);
  244. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2136315b);
  245. } else if (pixel_clock >= 297000) {
  246. /* 2.97Gbps */
  247. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303382);
  248. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2036315b);
  249. } else if (pixel_clock >= 148500) {
  250. /* 1.485Gbps */
  251. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303362);
  252. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2016315b);
  253. } else {
  254. /* 742.5Mbps, and below */
  255. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33604142);
  256. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x0016315b);
  257. }
  258. } else if (dw_hdmi_is_compatible(dw_hdmi,
  259. "amlogic,meson-gxbb-dw-hdmi")) {
  260. if (pixel_clock >= 371250) {
  261. /* 5.94Gbps, 3.7125Gbps */
  262. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33353245);
  263. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2100115b);
  264. } else if (pixel_clock >= 297000) {
  265. /* 2.97Gbps */
  266. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33634283);
  267. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0xb000115b);
  268. } else {
  269. /* 1.485Gbps, and below */
  270. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33632122);
  271. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2000115b);
  272. }
  273. } else if (dw_hdmi_is_compatible(dw_hdmi,
  274. "amlogic,meson-g12a-dw-hdmi")) {
  275. if (pixel_clock >= 371250) {
  276. /* 5.94Gbps, 3.7125Gbps */
  277. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x37eb65c4);
  278. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
  279. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x0000080b);
  280. } else if (pixel_clock >= 297000) {
  281. /* 2.97Gbps */
  282. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb6262);
  283. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
  284. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003);
  285. } else {
  286. /* 1.485Gbps, and below */
  287. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb4242);
  288. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
  289. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003);
  290. }
  291. }
  292. }
  293. static inline void meson_dw_hdmi_phy_reset(struct meson_dw_hdmi *dw_hdmi)
  294. {
  295. struct meson_drm *priv = dw_hdmi->priv;
  296. /* Enable and software reset */
  297. regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xf);
  298. mdelay(2);
  299. /* Enable and unreset */
  300. regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xe);
  301. mdelay(2);
  302. }
  303. static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
  304. const struct drm_display_info *display,
  305. const struct drm_display_mode *mode)
  306. {
  307. struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
  308. bool is_hdmi2_sink = display->hdmi.scdc.supported;
  309. struct meson_drm *priv = dw_hdmi->priv;
  310. unsigned int wr_clk =
  311. readl_relaxed(priv->io_base + _REG(VPU_HDMI_SETTING));
  312. bool mode_is_420 = false;
  313. DRM_DEBUG_DRIVER("\"%s\" div%d\n", mode->name,
  314. mode->clock > 340000 ? 40 : 10);
  315. if (drm_mode_is_420_only(display, mode) ||
  316. (!is_hdmi2_sink && drm_mode_is_420_also(display, mode)) ||
  317. dw_hdmi_bus_fmt_is_420(hdmi))
  318. mode_is_420 = true;
  319. /* TMDS pattern setup */
  320. if (mode->clock > 340000 && !mode_is_420) {
  321. dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
  322. 0);
  323. dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
  324. 0x03ff03ff);
  325. } else {
  326. dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
  327. 0x001f001f);
  328. dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
  329. 0x001f001f);
  330. }
  331. /* Load TMDS pattern */
  332. dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1);
  333. msleep(20);
  334. dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2);
  335. /* Setup PHY parameters */
  336. meson_hdmi_phy_setup_mode(dw_hdmi, mode, mode_is_420);
  337. /* Disable clock, fifo, fifo_wr */
  338. regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0);
  339. dw_hdmi_set_high_tmds_clock_ratio(hdmi, display);
  340. msleep(100);
  341. /* Reset PHY 3 times in a row */
  342. meson_dw_hdmi_phy_reset(dw_hdmi);
  343. meson_dw_hdmi_phy_reset(dw_hdmi);
  344. meson_dw_hdmi_phy_reset(dw_hdmi);
  345. /* Temporary Disable VENC video stream */
  346. if (priv->venc.hdmi_use_enci)
  347. writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN));
  348. else
  349. writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
  350. /* Temporary Disable HDMI video stream to HDMI-TX */
  351. writel_bits_relaxed(0x3, 0,
  352. priv->io_base + _REG(VPU_HDMI_SETTING));
  353. writel_bits_relaxed(0xf << 8, 0,
  354. priv->io_base + _REG(VPU_HDMI_SETTING));
  355. /* Re-Enable VENC video stream */
  356. if (priv->venc.hdmi_use_enci)
  357. writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN));
  358. else
  359. writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN));
  360. /* Push back HDMI clock settings */
  361. writel_bits_relaxed(0xf << 8, wr_clk & (0xf << 8),
  362. priv->io_base + _REG(VPU_HDMI_SETTING));
  363. /* Enable and Select HDMI video source for HDMI-TX */
  364. if (priv->venc.hdmi_use_enci)
  365. writel_bits_relaxed(0x3, MESON_VENC_SOURCE_ENCI,
  366. priv->io_base + _REG(VPU_HDMI_SETTING));
  367. else
  368. writel_bits_relaxed(0x3, MESON_VENC_SOURCE_ENCP,
  369. priv->io_base + _REG(VPU_HDMI_SETTING));
  370. return 0;
  371. }
  372. static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi,
  373. void *data)
  374. {
  375. struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
  376. struct meson_drm *priv = dw_hdmi->priv;
  377. DRM_DEBUG_DRIVER("\n");
  378. /* Fallback to init mode */
  379. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, dw_hdmi->data->cntl1_init);
  380. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, dw_hdmi->data->cntl0_init);
  381. }
  382. static enum drm_connector_status dw_hdmi_read_hpd(struct dw_hdmi *hdmi,
  383. void *data)
  384. {
  385. struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
  386. return !!dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_STAT0) ?
  387. connector_status_connected : connector_status_disconnected;
  388. }
  389. static void dw_hdmi_setup_hpd(struct dw_hdmi *hdmi,
  390. void *data)
  391. {
  392. struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
  393. /* Setup HPD Filter */
  394. dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_HPD_FILTER,
  395. (0xa << 12) | 0xa0);
  396. /* Clear interrupts */
  397. dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
  398. HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL);
  399. /* Unmask interrupts */
  400. dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_INTR_MASKN,
  401. HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL,
  402. HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL);
  403. }
  404. static const struct dw_hdmi_phy_ops meson_dw_hdmi_phy_ops = {
  405. .init = dw_hdmi_phy_init,
  406. .disable = dw_hdmi_phy_disable,
  407. .read_hpd = dw_hdmi_read_hpd,
  408. .setup_hpd = dw_hdmi_setup_hpd,
  409. };
  410. static irqreturn_t dw_hdmi_top_irq(int irq, void *dev_id)
  411. {
  412. struct meson_dw_hdmi *dw_hdmi = dev_id;
  413. u32 stat;
  414. stat = dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_INTR_STAT);
  415. dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, stat);
  416. /* HPD Events, handle in the threaded interrupt handler */
  417. if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) {
  418. dw_hdmi->irq_stat = stat;
  419. return IRQ_WAKE_THREAD;
  420. }
  421. /* HDMI Controller Interrupt */
  422. if (stat & 1)
  423. return IRQ_NONE;
  424. /* TOFIX Handle HDCP Interrupts */
  425. return IRQ_HANDLED;
  426. }
  427. /* Threaded interrupt handler to manage HPD events */
  428. static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id)
  429. {
  430. struct meson_dw_hdmi *dw_hdmi = dev_id;
  431. u32 stat = dw_hdmi->irq_stat;
  432. /* HPD Events */
  433. if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) {
  434. bool hpd_connected = false;
  435. if (stat & HDMITX_TOP_INTR_HPD_RISE)
  436. hpd_connected = true;
  437. dw_hdmi_setup_rx_sense(dw_hdmi->hdmi, hpd_connected,
  438. hpd_connected);
  439. drm_helper_hpd_irq_event(dw_hdmi->bridge->dev);
  440. drm_bridge_hpd_notify(dw_hdmi->bridge,
  441. hpd_connected ? connector_status_connected
  442. : connector_status_disconnected);
  443. }
  444. return IRQ_HANDLED;
  445. }
  446. /* DW HDMI Regmap */
  447. static int meson_dw_hdmi_reg_read(void *context, unsigned int reg,
  448. unsigned int *result)
  449. {
  450. struct meson_dw_hdmi *dw_hdmi = context;
  451. *result = dw_hdmi->data->dwc_read(dw_hdmi, reg);
  452. return 0;
  453. }
  454. static int meson_dw_hdmi_reg_write(void *context, unsigned int reg,
  455. unsigned int val)
  456. {
  457. struct meson_dw_hdmi *dw_hdmi = context;
  458. dw_hdmi->data->dwc_write(dw_hdmi, reg, val);
  459. return 0;
  460. }
  461. static const struct regmap_config meson_dw_hdmi_regmap_config = {
  462. .reg_bits = 32,
  463. .val_bits = 8,
  464. .reg_read = meson_dw_hdmi_reg_read,
  465. .reg_write = meson_dw_hdmi_reg_write,
  466. .max_register = 0x10000,
  467. .fast_io = true,
  468. };
  469. static const struct meson_dw_hdmi_data meson_dw_hdmi_gxbb_data = {
  470. .top_read = dw_hdmi_top_read,
  471. .top_write = dw_hdmi_top_write,
  472. .dwc_read = dw_hdmi_dwc_read,
  473. .dwc_write = dw_hdmi_dwc_write,
  474. .cntl0_init = 0x0,
  475. .cntl1_init = PHY_CNTL1_INIT | PHY_INVERT,
  476. };
  477. static const struct meson_dw_hdmi_data meson_dw_hdmi_gxl_data = {
  478. .top_read = dw_hdmi_top_read,
  479. .top_write = dw_hdmi_top_write,
  480. .dwc_read = dw_hdmi_dwc_read,
  481. .dwc_write = dw_hdmi_dwc_write,
  482. .cntl0_init = 0x0,
  483. .cntl1_init = PHY_CNTL1_INIT,
  484. };
  485. static const struct meson_dw_hdmi_data meson_dw_hdmi_g12a_data = {
  486. .top_read = dw_hdmi_g12a_top_read,
  487. .top_write = dw_hdmi_g12a_top_write,
  488. .dwc_read = dw_hdmi_g12a_dwc_read,
  489. .dwc_write = dw_hdmi_g12a_dwc_write,
  490. .cntl0_init = 0x000b4242, /* Bandgap */
  491. .cntl1_init = PHY_CNTL1_INIT,
  492. };
  493. static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi)
  494. {
  495. struct meson_drm *priv = meson_dw_hdmi->priv;
  496. /* Enable clocks */
  497. regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100);
  498. /* Bring HDMITX MEM output of power down */
  499. regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0);
  500. /* Reset HDMITX APB & TX & PHY */
  501. reset_control_reset(meson_dw_hdmi->hdmitx_apb);
  502. reset_control_reset(meson_dw_hdmi->hdmitx_ctrl);
  503. reset_control_reset(meson_dw_hdmi->hdmitx_phy);
  504. /* Enable APB3 fail on error */
  505. if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
  506. writel_bits_relaxed(BIT(15), BIT(15),
  507. meson_dw_hdmi->hdmitx + HDMITX_TOP_CTRL_REG);
  508. writel_bits_relaxed(BIT(15), BIT(15),
  509. meson_dw_hdmi->hdmitx + HDMITX_DWC_CTRL_REG);
  510. }
  511. /* Bring out of reset */
  512. meson_dw_hdmi->data->top_write(meson_dw_hdmi,
  513. HDMITX_TOP_SW_RESET, 0);
  514. msleep(20);
  515. meson_dw_hdmi->data->top_write(meson_dw_hdmi,
  516. HDMITX_TOP_CLK_CNTL, 0xff);
  517. /* Enable normal output to PHY */
  518. meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
  519. /* Setup PHY */
  520. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, meson_dw_hdmi->data->cntl1_init);
  521. regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, meson_dw_hdmi->data->cntl0_init);
  522. /* Enable HDMI-TX Interrupt */
  523. meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
  524. HDMITX_TOP_INTR_CORE);
  525. meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_MASKN,
  526. HDMITX_TOP_INTR_CORE);
  527. }
  528. static void meson_disable_clk(void *data)
  529. {
  530. clk_disable_unprepare(data);
  531. }
  532. static int meson_enable_clk(struct device *dev, char *name)
  533. {
  534. struct clk *clk;
  535. int ret;
  536. clk = devm_clk_get(dev, name);
  537. if (IS_ERR(clk)) {
  538. dev_err(dev, "Unable to get %s pclk\n", name);
  539. return PTR_ERR(clk);
  540. }
  541. ret = clk_prepare_enable(clk);
  542. if (!ret)
  543. ret = devm_add_action_or_reset(dev, meson_disable_clk, clk);
  544. return ret;
  545. }
  546. static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
  547. void *data)
  548. {
  549. struct platform_device *pdev = to_platform_device(dev);
  550. const struct meson_dw_hdmi_data *match;
  551. struct meson_dw_hdmi *meson_dw_hdmi;
  552. struct drm_device *drm = data;
  553. struct meson_drm *priv = drm->dev_private;
  554. struct dw_hdmi_plat_data *dw_plat_data;
  555. int irq;
  556. int ret;
  557. DRM_DEBUG_DRIVER("\n");
  558. match = of_device_get_match_data(&pdev->dev);
  559. if (!match) {
  560. dev_err(&pdev->dev, "failed to get match data\n");
  561. return -ENODEV;
  562. }
  563. meson_dw_hdmi = devm_kzalloc(dev, sizeof(*meson_dw_hdmi),
  564. GFP_KERNEL);
  565. if (!meson_dw_hdmi)
  566. return -ENOMEM;
  567. meson_dw_hdmi->priv = priv;
  568. meson_dw_hdmi->dev = dev;
  569. meson_dw_hdmi->data = match;
  570. dw_plat_data = &meson_dw_hdmi->dw_plat_data;
  571. ret = devm_regulator_get_enable_optional(dev, "hdmi");
  572. if (ret < 0 && ret != -ENODEV)
  573. return ret;
  574. meson_dw_hdmi->hdmitx_apb = devm_reset_control_get_exclusive(dev,
  575. "hdmitx_apb");
  576. if (IS_ERR(meson_dw_hdmi->hdmitx_apb)) {
  577. dev_err(dev, "Failed to get hdmitx_apb reset\n");
  578. return PTR_ERR(meson_dw_hdmi->hdmitx_apb);
  579. }
  580. meson_dw_hdmi->hdmitx_ctrl = devm_reset_control_get_exclusive(dev,
  581. "hdmitx");
  582. if (IS_ERR(meson_dw_hdmi->hdmitx_ctrl)) {
  583. dev_err(dev, "Failed to get hdmitx reset\n");
  584. return PTR_ERR(meson_dw_hdmi->hdmitx_ctrl);
  585. }
  586. meson_dw_hdmi->hdmitx_phy = devm_reset_control_get_exclusive(dev,
  587. "hdmitx_phy");
  588. if (IS_ERR(meson_dw_hdmi->hdmitx_phy)) {
  589. dev_err(dev, "Failed to get hdmitx_phy reset\n");
  590. return PTR_ERR(meson_dw_hdmi->hdmitx_phy);
  591. }
  592. meson_dw_hdmi->hdmitx = devm_platform_ioremap_resource(pdev, 0);
  593. if (IS_ERR(meson_dw_hdmi->hdmitx))
  594. return PTR_ERR(meson_dw_hdmi->hdmitx);
  595. ret = meson_enable_clk(dev, "isfr");
  596. if (ret)
  597. return ret;
  598. ret = meson_enable_clk(dev, "iahb");
  599. if (ret)
  600. return ret;
  601. ret = meson_enable_clk(dev, "venci");
  602. if (ret)
  603. return ret;
  604. dw_plat_data->regm = devm_regmap_init(dev, NULL, meson_dw_hdmi,
  605. &meson_dw_hdmi_regmap_config);
  606. if (IS_ERR(dw_plat_data->regm))
  607. return PTR_ERR(dw_plat_data->regm);
  608. irq = platform_get_irq(pdev, 0);
  609. if (irq < 0)
  610. return irq;
  611. ret = devm_request_threaded_irq(dev, irq, dw_hdmi_top_irq,
  612. dw_hdmi_top_thread_irq, IRQF_SHARED,
  613. "dw_hdmi_top_irq", meson_dw_hdmi);
  614. if (ret) {
  615. dev_err(dev, "Failed to request hdmi top irq\n");
  616. return ret;
  617. }
  618. meson_dw_hdmi_init(meson_dw_hdmi);
  619. /* Bridge / Connector */
  620. dw_plat_data->priv_data = meson_dw_hdmi;
  621. dw_plat_data->phy_ops = &meson_dw_hdmi_phy_ops;
  622. dw_plat_data->phy_name = "meson_dw_hdmi_phy";
  623. dw_plat_data->phy_data = meson_dw_hdmi;
  624. dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;
  625. dw_plat_data->ycbcr_420_allowed = true;
  626. dw_plat_data->disable_cec = true;
  627. dw_plat_data->output_port = 1;
  628. if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
  629. dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
  630. dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-g12a-dw-hdmi"))
  631. dw_plat_data->use_drm_infoframe = true;
  632. platform_set_drvdata(pdev, meson_dw_hdmi);
  633. meson_dw_hdmi->hdmi = dw_hdmi_probe(pdev, &meson_dw_hdmi->dw_plat_data);
  634. if (IS_ERR(meson_dw_hdmi->hdmi))
  635. return PTR_ERR(meson_dw_hdmi->hdmi);
  636. meson_dw_hdmi->bridge = of_drm_find_and_get_bridge(pdev->dev.of_node);
  637. DRM_DEBUG_DRIVER("HDMI controller initialized\n");
  638. return 0;
  639. }
  640. static void meson_dw_hdmi_unbind(struct device *dev, struct device *master,
  641. void *data)
  642. {
  643. struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev);
  644. struct platform_device *pdev = to_platform_device(dev);
  645. int irq = platform_get_irq(pdev, 0);
  646. devm_free_irq(dev, irq, meson_dw_hdmi);
  647. dw_hdmi_unbind(meson_dw_hdmi->hdmi);
  648. drm_bridge_put(meson_dw_hdmi->bridge);
  649. }
  650. static const struct component_ops meson_dw_hdmi_ops = {
  651. .bind = meson_dw_hdmi_bind,
  652. .unbind = meson_dw_hdmi_unbind,
  653. };
  654. static int __maybe_unused meson_dw_hdmi_pm_suspend(struct device *dev)
  655. {
  656. struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev);
  657. if (!meson_dw_hdmi)
  658. return 0;
  659. /* Reset TOP */
  660. meson_dw_hdmi->data->top_write(meson_dw_hdmi,
  661. HDMITX_TOP_SW_RESET, 0);
  662. return 0;
  663. }
  664. static int __maybe_unused meson_dw_hdmi_pm_resume(struct device *dev)
  665. {
  666. struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev);
  667. if (!meson_dw_hdmi)
  668. return 0;
  669. meson_dw_hdmi_init(meson_dw_hdmi);
  670. dw_hdmi_resume(meson_dw_hdmi->hdmi);
  671. return 0;
  672. }
  673. static int meson_dw_hdmi_probe(struct platform_device *pdev)
  674. {
  675. return component_add(&pdev->dev, &meson_dw_hdmi_ops);
  676. }
  677. static void meson_dw_hdmi_remove(struct platform_device *pdev)
  678. {
  679. component_del(&pdev->dev, &meson_dw_hdmi_ops);
  680. }
  681. static const struct dev_pm_ops meson_dw_hdmi_pm_ops = {
  682. SET_SYSTEM_SLEEP_PM_OPS(meson_dw_hdmi_pm_suspend,
  683. meson_dw_hdmi_pm_resume)
  684. };
  685. static const struct of_device_id meson_dw_hdmi_of_table[] = {
  686. { .compatible = "amlogic,meson-gxbb-dw-hdmi",
  687. .data = &meson_dw_hdmi_gxbb_data },
  688. { .compatible = "amlogic,meson-gxl-dw-hdmi",
  689. .data = &meson_dw_hdmi_gxl_data },
  690. { .compatible = "amlogic,meson-gxm-dw-hdmi",
  691. .data = &meson_dw_hdmi_gxl_data },
  692. { .compatible = "amlogic,meson-g12a-dw-hdmi",
  693. .data = &meson_dw_hdmi_g12a_data },
  694. { }
  695. };
  696. MODULE_DEVICE_TABLE(of, meson_dw_hdmi_of_table);
  697. static struct platform_driver meson_dw_hdmi_platform_driver = {
  698. .probe = meson_dw_hdmi_probe,
  699. .remove = meson_dw_hdmi_remove,
  700. .driver = {
  701. .name = DRIVER_NAME,
  702. .of_match_table = meson_dw_hdmi_of_table,
  703. .pm = &meson_dw_hdmi_pm_ops,
  704. },
  705. };
  706. module_platform_driver(meson_dw_hdmi_platform_driver);
  707. MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
  708. MODULE_DESCRIPTION(DRIVER_DESC);
  709. MODULE_LICENSE("GPL");