mtk_disp_ovl.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * Copyright (c) 2015 MediaTek Inc.
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 as
  6. * published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. */
  13. #include <drm/drmP.h>
  14. #include <linux/clk.h>
  15. #include <linux/component.h>
  16. #include <linux/of_device.h>
  17. #include <linux/of_irq.h>
  18. #include <linux/platform_device.h>
  19. #include "mtk_drm_crtc.h"
  20. #include "mtk_drm_ddp_comp.h"
  21. #define DISP_REG_OVL_INTEN 0x0004
  22. #define OVL_FME_CPL_INT BIT(1)
  23. #define DISP_REG_OVL_INTSTA 0x0008
  24. #define DISP_REG_OVL_EN 0x000c
  25. #define DISP_REG_OVL_RST 0x0014
  26. #define DISP_REG_OVL_ROI_SIZE 0x0020
  27. #define DISP_REG_OVL_ROI_BGCLR 0x0028
  28. #define DISP_REG_OVL_SRC_CON 0x002c
  29. #define DISP_REG_OVL_CON(n) (0x0030 + 0x20 * (n))
  30. #define DISP_REG_OVL_SRC_SIZE(n) (0x0038 + 0x20 * (n))
  31. #define DISP_REG_OVL_OFFSET(n) (0x003c + 0x20 * (n))
  32. #define DISP_REG_OVL_PITCH(n) (0x0044 + 0x20 * (n))
  33. #define DISP_REG_OVL_RDMA_CTRL(n) (0x00c0 + 0x20 * (n))
  34. #define DISP_REG_OVL_RDMA_GMC(n) (0x00c8 + 0x20 * (n))
  35. #define DISP_REG_OVL_ADDR_MT2701 0x0040
  36. #define DISP_REG_OVL_ADDR_MT8173 0x0f40
  37. #define DISP_REG_OVL_ADDR(ovl, n) ((ovl)->data->addr + 0x20 * (n))
  38. #define OVL_RDMA_MEM_GMC 0x40402020
  39. #define OVL_CON_BYTE_SWAP BIT(24)
  40. #define OVL_CON_MTX_YUV_TO_RGB (6 << 16)
  41. #define OVL_CON_CLRFMT_RGB (1 << 12)
  42. #define OVL_CON_CLRFMT_RGBA8888 (2 << 12)
  43. #define OVL_CON_CLRFMT_ARGB8888 (3 << 12)
  44. #define OVL_CON_CLRFMT_UYVY (4 << 12)
  45. #define OVL_CON_CLRFMT_YUYV (5 << 12)
  46. #define OVL_CON_CLRFMT_RGB565(ovl) ((ovl)->data->fmt_rgb565_is_0 ? \
  47. 0 : OVL_CON_CLRFMT_RGB)
  48. #define OVL_CON_CLRFMT_RGB888(ovl) ((ovl)->data->fmt_rgb565_is_0 ? \
  49. OVL_CON_CLRFMT_RGB : 0)
  50. #define OVL_CON_AEN BIT(8)
  51. #define OVL_CON_ALPHA 0xff
  52. struct mtk_disp_ovl_data {
  53. unsigned int addr;
  54. bool fmt_rgb565_is_0;
  55. };
  56. /**
  57. * struct mtk_disp_ovl - DISP_OVL driver structure
  58. * @ddp_comp - structure containing type enum and hardware resources
  59. * @crtc - associated crtc to report vblank events to
  60. */
  61. struct mtk_disp_ovl {
  62. struct mtk_ddp_comp ddp_comp;
  63. struct drm_crtc *crtc;
  64. const struct mtk_disp_ovl_data *data;
  65. };
  66. static inline struct mtk_disp_ovl *comp_to_ovl(struct mtk_ddp_comp *comp)
  67. {
  68. return container_of(comp, struct mtk_disp_ovl, ddp_comp);
  69. }
  70. static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id)
  71. {
  72. struct mtk_disp_ovl *priv = dev_id;
  73. struct mtk_ddp_comp *ovl = &priv->ddp_comp;
  74. /* Clear frame completion interrupt */
  75. writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA);
  76. if (!priv->crtc)
  77. return IRQ_NONE;
  78. mtk_crtc_ddp_irq(priv->crtc, ovl);
  79. return IRQ_HANDLED;
  80. }
  81. static void mtk_ovl_enable_vblank(struct mtk_ddp_comp *comp,
  82. struct drm_crtc *crtc)
  83. {
  84. struct mtk_disp_ovl *ovl = comp_to_ovl(comp);
  85. ovl->crtc = crtc;
  86. writel(0x0, comp->regs + DISP_REG_OVL_INTSTA);
  87. writel_relaxed(OVL_FME_CPL_INT, comp->regs + DISP_REG_OVL_INTEN);
  88. }
  89. static void mtk_ovl_disable_vblank(struct mtk_ddp_comp *comp)
  90. {
  91. struct mtk_disp_ovl *ovl = comp_to_ovl(comp);
  92. ovl->crtc = NULL;
  93. writel_relaxed(0x0, comp->regs + DISP_REG_OVL_INTEN);
  94. }
  95. static void mtk_ovl_start(struct mtk_ddp_comp *comp)
  96. {
  97. writel_relaxed(0x1, comp->regs + DISP_REG_OVL_EN);
  98. }
  99. static void mtk_ovl_stop(struct mtk_ddp_comp *comp)
  100. {
  101. writel_relaxed(0x0, comp->regs + DISP_REG_OVL_EN);
  102. }
  103. static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
  104. unsigned int h, unsigned int vrefresh,
  105. unsigned int bpc)
  106. {
  107. if (w != 0 && h != 0)
  108. writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE);
  109. writel_relaxed(0x0, comp->regs + DISP_REG_OVL_ROI_BGCLR);
  110. writel(0x1, comp->regs + DISP_REG_OVL_RST);
  111. writel(0x0, comp->regs + DISP_REG_OVL_RST);
  112. }
  113. static unsigned int mtk_ovl_layer_nr(struct mtk_ddp_comp *comp)
  114. {
  115. return 4;
  116. }
  117. static void mtk_ovl_layer_on(struct mtk_ddp_comp *comp, unsigned int idx)
  118. {
  119. unsigned int reg;
  120. writel(0x1, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
  121. writel(OVL_RDMA_MEM_GMC, comp->regs + DISP_REG_OVL_RDMA_GMC(idx));
  122. reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
  123. reg = reg | BIT(idx);
  124. writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
  125. }
  126. static void mtk_ovl_layer_off(struct mtk_ddp_comp *comp, unsigned int idx)
  127. {
  128. unsigned int reg;
  129. reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
  130. reg = reg & ~BIT(idx);
  131. writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
  132. writel(0x0, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
  133. }
  134. static unsigned int ovl_fmt_convert(struct mtk_disp_ovl *ovl, unsigned int fmt)
  135. {
  136. /* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX"
  137. * is defined in mediatek HW data sheet.
  138. * The alphabet order in XXX is no relation to data
  139. * arrangement in memory.
  140. */
  141. switch (fmt) {
  142. default:
  143. case DRM_FORMAT_RGB565:
  144. return OVL_CON_CLRFMT_RGB565(ovl);
  145. case DRM_FORMAT_BGR565:
  146. return OVL_CON_CLRFMT_RGB565(ovl) | OVL_CON_BYTE_SWAP;
  147. case DRM_FORMAT_RGB888:
  148. return OVL_CON_CLRFMT_RGB888(ovl);
  149. case DRM_FORMAT_BGR888:
  150. return OVL_CON_CLRFMT_RGB888(ovl) | OVL_CON_BYTE_SWAP;
  151. case DRM_FORMAT_RGBX8888:
  152. case DRM_FORMAT_RGBA8888:
  153. return OVL_CON_CLRFMT_ARGB8888;
  154. case DRM_FORMAT_BGRX8888:
  155. case DRM_FORMAT_BGRA8888:
  156. return OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP;
  157. case DRM_FORMAT_XRGB8888:
  158. case DRM_FORMAT_ARGB8888:
  159. return OVL_CON_CLRFMT_RGBA8888;
  160. case DRM_FORMAT_XBGR8888:
  161. case DRM_FORMAT_ABGR8888:
  162. return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP;
  163. case DRM_FORMAT_UYVY:
  164. return OVL_CON_CLRFMT_UYVY | OVL_CON_MTX_YUV_TO_RGB;
  165. case DRM_FORMAT_YUYV:
  166. return OVL_CON_CLRFMT_YUYV | OVL_CON_MTX_YUV_TO_RGB;
  167. }
  168. }
  169. static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx,
  170. struct mtk_plane_state *state)
  171. {
  172. struct mtk_disp_ovl *ovl = comp_to_ovl(comp);
  173. struct mtk_plane_pending_state *pending = &state->pending;
  174. unsigned int addr = pending->addr;
  175. unsigned int pitch = pending->pitch & 0xffff;
  176. unsigned int fmt = pending->format;
  177. unsigned int offset = (pending->y << 16) | pending->x;
  178. unsigned int src_size = (pending->height << 16) | pending->width;
  179. unsigned int con;
  180. if (!pending->enable)
  181. mtk_ovl_layer_off(comp, idx);
  182. con = ovl_fmt_convert(ovl, fmt);
  183. if (idx != 0)
  184. con |= OVL_CON_AEN | OVL_CON_ALPHA;
  185. writel_relaxed(con, comp->regs + DISP_REG_OVL_CON(idx));
  186. writel_relaxed(pitch, comp->regs + DISP_REG_OVL_PITCH(idx));
  187. writel_relaxed(src_size, comp->regs + DISP_REG_OVL_SRC_SIZE(idx));
  188. writel_relaxed(offset, comp->regs + DISP_REG_OVL_OFFSET(idx));
  189. writel_relaxed(addr, comp->regs + DISP_REG_OVL_ADDR(ovl, idx));
  190. if (pending->enable)
  191. mtk_ovl_layer_on(comp, idx);
  192. }
  193. static const struct mtk_ddp_comp_funcs mtk_disp_ovl_funcs = {
  194. .config = mtk_ovl_config,
  195. .start = mtk_ovl_start,
  196. .stop = mtk_ovl_stop,
  197. .enable_vblank = mtk_ovl_enable_vblank,
  198. .disable_vblank = mtk_ovl_disable_vblank,
  199. .layer_nr = mtk_ovl_layer_nr,
  200. .layer_on = mtk_ovl_layer_on,
  201. .layer_off = mtk_ovl_layer_off,
  202. .layer_config = mtk_ovl_layer_config,
  203. };
  204. static int mtk_disp_ovl_bind(struct device *dev, struct device *master,
  205. void *data)
  206. {
  207. struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
  208. struct drm_device *drm_dev = data;
  209. int ret;
  210. ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
  211. if (ret < 0) {
  212. dev_err(dev, "Failed to register component %pOF: %d\n",
  213. dev->of_node, ret);
  214. return ret;
  215. }
  216. return 0;
  217. }
  218. static void mtk_disp_ovl_unbind(struct device *dev, struct device *master,
  219. void *data)
  220. {
  221. struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
  222. struct drm_device *drm_dev = data;
  223. mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
  224. }
  225. static const struct component_ops mtk_disp_ovl_component_ops = {
  226. .bind = mtk_disp_ovl_bind,
  227. .unbind = mtk_disp_ovl_unbind,
  228. };
  229. static int mtk_disp_ovl_probe(struct platform_device *pdev)
  230. {
  231. struct device *dev = &pdev->dev;
  232. struct mtk_disp_ovl *priv;
  233. int comp_id;
  234. int irq;
  235. int ret;
  236. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  237. if (!priv)
  238. return -ENOMEM;
  239. irq = platform_get_irq(pdev, 0);
  240. if (irq < 0)
  241. return irq;
  242. comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_OVL);
  243. if (comp_id < 0) {
  244. dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
  245. return comp_id;
  246. }
  247. ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
  248. &mtk_disp_ovl_funcs);
  249. if (ret) {
  250. dev_err(dev, "Failed to initialize component: %d\n", ret);
  251. return ret;
  252. }
  253. priv->data = of_device_get_match_data(dev);
  254. platform_set_drvdata(pdev, priv);
  255. ret = devm_request_irq(dev, irq, mtk_disp_ovl_irq_handler,
  256. IRQF_TRIGGER_NONE, dev_name(dev), priv);
  257. if (ret < 0) {
  258. dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
  259. return ret;
  260. }
  261. ret = component_add(dev, &mtk_disp_ovl_component_ops);
  262. if (ret)
  263. dev_err(dev, "Failed to add component: %d\n", ret);
  264. return ret;
  265. }
  266. static int mtk_disp_ovl_remove(struct platform_device *pdev)
  267. {
  268. component_del(&pdev->dev, &mtk_disp_ovl_component_ops);
  269. return 0;
  270. }
  271. static const struct mtk_disp_ovl_data mt2701_ovl_driver_data = {
  272. .addr = DISP_REG_OVL_ADDR_MT2701,
  273. .fmt_rgb565_is_0 = false,
  274. };
  275. static const struct mtk_disp_ovl_data mt8173_ovl_driver_data = {
  276. .addr = DISP_REG_OVL_ADDR_MT8173,
  277. .fmt_rgb565_is_0 = true,
  278. };
  279. static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
  280. { .compatible = "mediatek,mt2701-disp-ovl",
  281. .data = &mt2701_ovl_driver_data},
  282. { .compatible = "mediatek,mt8173-disp-ovl",
  283. .data = &mt8173_ovl_driver_data},
  284. {},
  285. };
  286. MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
  287. struct platform_driver mtk_disp_ovl_driver = {
  288. .probe = mtk_disp_ovl_probe,
  289. .remove = mtk_disp_ovl_remove,
  290. .driver = {
  291. .name = "mediatek-disp-ovl",
  292. .owner = THIS_MODULE,
  293. .of_match_table = mtk_disp_ovl_driver_dt_match,
  294. },
  295. };