ipu-dp.c 8.0 KB

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