ipu-dp.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (c) 2010 Sascha Hauer <[email protected]>
  4. * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
  5. */
  6. #include <linux/export.h>
  7. #include <linux/kernel.h>
  8. #include <linux/types.h>
  9. #include <linux/errno.h>
  10. #include <linux/io.h>
  11. #include <linux/err.h>
  12. #include <drm/drm_color_mgmt.h>
  13. #include <video/imx-ipu-v3.h>
  14. #include "ipu-prv.h"
  15. #define DP_SYNC 0
  16. #define DP_ASYNC0 0x60
  17. #define DP_ASYNC1 0xBC
  18. #define DP_COM_CONF 0x0
  19. #define DP_GRAPH_WIND_CTRL 0x0004
  20. #define DP_FG_POS 0x0008
  21. #define DP_CSC_A_0 0x0044
  22. #define DP_CSC_A_1 0x0048
  23. #define DP_CSC_A_2 0x004C
  24. #define DP_CSC_A_3 0x0050
  25. #define DP_CSC_0 0x0054
  26. #define DP_CSC_1 0x0058
  27. #define DP_COM_CONF_FG_EN (1 << 0)
  28. #define DP_COM_CONF_GWSEL (1 << 1)
  29. #define DP_COM_CONF_GWAM (1 << 2)
  30. #define DP_COM_CONF_GWCKE (1 << 3)
  31. #define DP_COM_CONF_CSC_DEF_MASK (3 << 8)
  32. #define DP_COM_CONF_CSC_DEF_OFFSET 8
  33. #define DP_COM_CONF_CSC_DEF_FG (3 << 8)
  34. #define DP_COM_CONF_CSC_DEF_BG (2 << 8)
  35. #define DP_COM_CONF_CSC_DEF_BOTH (1 << 8)
  36. #define IPUV3_NUM_FLOWS 3
  37. struct ipu_dp_priv;
  38. struct ipu_dp {
  39. u32 flow;
  40. bool in_use;
  41. bool foreground;
  42. enum ipu_color_space in_cs;
  43. };
  44. struct ipu_flow {
  45. struct ipu_dp foreground;
  46. struct ipu_dp background;
  47. enum ipu_color_space out_cs;
  48. void __iomem *base;
  49. struct ipu_dp_priv *priv;
  50. };
  51. struct ipu_dp_priv {
  52. struct ipu_soc *ipu;
  53. struct device *dev;
  54. void __iomem *base;
  55. struct ipu_flow flow[IPUV3_NUM_FLOWS];
  56. struct mutex mutex;
  57. int use_count;
  58. };
  59. static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
  60. static inline struct ipu_flow *to_flow(struct ipu_dp *dp)
  61. {
  62. if (dp->foreground)
  63. return container_of(dp, struct ipu_flow, foreground);
  64. else
  65. return container_of(dp, struct ipu_flow, background);
  66. }
  67. int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
  68. u8 alpha, bool bg_chan)
  69. {
  70. struct ipu_flow *flow = to_flow(dp);
  71. struct ipu_dp_priv *priv = flow->priv;
  72. u32 reg;
  73. mutex_lock(&priv->mutex);
  74. reg = readl(flow->base + DP_COM_CONF);
  75. if (bg_chan)
  76. reg &= ~DP_COM_CONF_GWSEL;
  77. else
  78. reg |= DP_COM_CONF_GWSEL;
  79. writel(reg, flow->base + DP_COM_CONF);
  80. if (enable) {
  81. reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL;
  82. writel(reg | ((u32) alpha << 24),
  83. flow->base + DP_GRAPH_WIND_CTRL);
  84. reg = readl(flow->base + DP_COM_CONF);
  85. writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
  86. } else {
  87. reg = readl(flow->base + DP_COM_CONF);
  88. writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
  89. }
  90. ipu_srm_dp_update(priv->ipu, true);
  91. mutex_unlock(&priv->mutex);
  92. return 0;
  93. }
  94. EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
  95. int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
  96. {
  97. struct ipu_flow *flow = to_flow(dp);
  98. struct ipu_dp_priv *priv = flow->priv;
  99. writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS);
  100. ipu_srm_dp_update(priv->ipu, true);
  101. return 0;
  102. }
  103. EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
  104. static void ipu_dp_csc_init(struct ipu_flow *flow,
  105. enum drm_color_encoding ycbcr_enc,
  106. enum drm_color_range range,
  107. enum ipu_color_space in,
  108. enum ipu_color_space out,
  109. u32 place)
  110. {
  111. u32 reg;
  112. reg = readl(flow->base + DP_COM_CONF);
  113. reg &= ~DP_COM_CONF_CSC_DEF_MASK;
  114. if (in == out) {
  115. writel(reg, flow->base + DP_COM_CONF);
  116. return;
  117. }
  118. if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) {
  119. writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0);
  120. writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1);
  121. writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2);
  122. writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3);
  123. writel(0x3d6 | (0x0000 << 16) | (2 << 30),
  124. flow->base + DP_CSC_0);
  125. writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
  126. flow->base + DP_CSC_1);
  127. } else if (ycbcr_enc == DRM_COLOR_YCBCR_BT709) {
  128. /* Rec.709 limited range */
  129. writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
  130. writel(0x0e5 | (0x095 << 16), flow->base + DP_CSC_A_1);
  131. writel(0x3e5 | (0x3bc << 16), flow->base + DP_CSC_A_2);
  132. writel(0x095 | (0x10e << 16), flow->base + DP_CSC_A_3);
  133. writel(0x000 | (0x3e10 << 16) | (1 << 30),
  134. flow->base + DP_CSC_0);
  135. writel(0x09a | (1 << 14) | (0x3dbe << 16) | (1 << 30),
  136. flow->base + DP_CSC_1);
  137. } else {
  138. /* BT.601 limited range */
  139. writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
  140. writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1);
  141. writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2);
  142. writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3);
  143. writel(0x000 | (0x3e42 << 16) | (1 << 30),
  144. flow->base + DP_CSC_0);
  145. writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
  146. flow->base + DP_CSC_1);
  147. }
  148. reg |= place;
  149. writel(reg, flow->base + DP_COM_CONF);
  150. }
  151. int ipu_dp_setup_channel(struct ipu_dp *dp,
  152. enum drm_color_encoding ycbcr_enc,
  153. enum drm_color_range range,
  154. enum ipu_color_space in,
  155. enum ipu_color_space out)
  156. {
  157. struct ipu_flow *flow = to_flow(dp);
  158. struct ipu_dp_priv *priv = flow->priv;
  159. mutex_lock(&priv->mutex);
  160. dp->in_cs = in;
  161. if (!dp->foreground)
  162. flow->out_cs = out;
  163. if (flow->foreground.in_cs == flow->background.in_cs) {
  164. /*
  165. * foreground and background are of same colorspace, put
  166. * colorspace converter after combining unit.
  167. */
  168. ipu_dp_csc_init(flow, ycbcr_enc, range,
  169. flow->foreground.in_cs, flow->out_cs,
  170. DP_COM_CONF_CSC_DEF_BOTH);
  171. } else {
  172. if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN ||
  173. flow->foreground.in_cs == flow->out_cs)
  174. /*
  175. * foreground identical to output, apply color
  176. * conversion on background
  177. */
  178. ipu_dp_csc_init(flow, ycbcr_enc, range,
  179. flow->background.in_cs,
  180. flow->out_cs, DP_COM_CONF_CSC_DEF_BG);
  181. else
  182. ipu_dp_csc_init(flow, ycbcr_enc, range,
  183. flow->foreground.in_cs,
  184. flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
  185. }
  186. ipu_srm_dp_update(priv->ipu, true);
  187. mutex_unlock(&priv->mutex);
  188. return 0;
  189. }
  190. EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
  191. int ipu_dp_enable(struct ipu_soc *ipu)
  192. {
  193. struct ipu_dp_priv *priv = ipu->dp_priv;
  194. mutex_lock(&priv->mutex);
  195. if (!priv->use_count)
  196. ipu_module_enable(priv->ipu, IPU_CONF_DP_EN);
  197. priv->use_count++;
  198. mutex_unlock(&priv->mutex);
  199. return 0;
  200. }
  201. EXPORT_SYMBOL_GPL(ipu_dp_enable);
  202. int ipu_dp_enable_channel(struct ipu_dp *dp)
  203. {
  204. struct ipu_flow *flow = to_flow(dp);
  205. struct ipu_dp_priv *priv = flow->priv;
  206. u32 reg;
  207. if (!dp->foreground)
  208. return 0;
  209. mutex_lock(&priv->mutex);
  210. reg = readl(flow->base + DP_COM_CONF);
  211. reg |= DP_COM_CONF_FG_EN;
  212. writel(reg, flow->base + DP_COM_CONF);
  213. ipu_srm_dp_update(priv->ipu, true);
  214. mutex_unlock(&priv->mutex);
  215. return 0;
  216. }
  217. EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
  218. void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync)
  219. {
  220. struct ipu_flow *flow = to_flow(dp);
  221. struct ipu_dp_priv *priv = flow->priv;
  222. u32 reg, csc;
  223. dp->in_cs = IPUV3_COLORSPACE_UNKNOWN;
  224. if (!dp->foreground)
  225. return;
  226. mutex_lock(&priv->mutex);
  227. reg = readl(flow->base + DP_COM_CONF);
  228. csc = reg & DP_COM_CONF_CSC_DEF_MASK;
  229. reg &= ~DP_COM_CONF_CSC_DEF_MASK;
  230. if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG)
  231. reg |= DP_COM_CONF_CSC_DEF_BG;
  232. reg &= ~DP_COM_CONF_FG_EN;
  233. writel(reg, flow->base + DP_COM_CONF);
  234. writel(0, flow->base + DP_FG_POS);
  235. ipu_srm_dp_update(priv->ipu, sync);
  236. mutex_unlock(&priv->mutex);
  237. }
  238. EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
  239. void ipu_dp_disable(struct ipu_soc *ipu)
  240. {
  241. struct ipu_dp_priv *priv = ipu->dp_priv;
  242. mutex_lock(&priv->mutex);
  243. priv->use_count--;
  244. if (!priv->use_count)
  245. ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
  246. if (priv->use_count < 0)
  247. priv->use_count = 0;
  248. mutex_unlock(&priv->mutex);
  249. }
  250. EXPORT_SYMBOL_GPL(ipu_dp_disable);
  251. struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
  252. {
  253. struct ipu_dp_priv *priv = ipu->dp_priv;
  254. struct ipu_dp *dp;
  255. if ((flow >> 1) >= IPUV3_NUM_FLOWS)
  256. return ERR_PTR(-EINVAL);
  257. if (flow & 1)
  258. dp = &priv->flow[flow >> 1].foreground;
  259. else
  260. dp = &priv->flow[flow >> 1].background;
  261. if (dp->in_use)
  262. return ERR_PTR(-EBUSY);
  263. dp->in_use = true;
  264. return dp;
  265. }
  266. EXPORT_SYMBOL_GPL(ipu_dp_get);
  267. void ipu_dp_put(struct ipu_dp *dp)
  268. {
  269. dp->in_use = false;
  270. }
  271. EXPORT_SYMBOL_GPL(ipu_dp_put);
  272. int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base)
  273. {
  274. struct ipu_dp_priv *priv;
  275. int i;
  276. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  277. if (!priv)
  278. return -ENOMEM;
  279. priv->dev = dev;
  280. priv->ipu = ipu;
  281. ipu->dp_priv = priv;
  282. priv->base = devm_ioremap(dev, base, PAGE_SIZE);
  283. if (!priv->base)
  284. return -ENOMEM;
  285. mutex_init(&priv->mutex);
  286. for (i = 0; i < IPUV3_NUM_FLOWS; i++) {
  287. priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN;
  288. priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN;
  289. priv->flow[i].foreground.foreground = true;
  290. priv->flow[i].base = priv->base + ipu_dp_flow_base[i];
  291. priv->flow[i].priv = priv;
  292. }
  293. return 0;
  294. }
  295. void ipu_dp_exit(struct ipu_soc *ipu)
  296. {
  297. }