xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-dp.c (revision b3fd7368f8f60bc9a7ffc2a5311db5f4dbd42180)
1  /*
2   * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
3   * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
4   *
5   * This program is free software; you can redistribute it and/or modify it
6   * under the terms of the GNU General Public License as published by the
7   * Free Software Foundation; either version 2 of the License, or (at your
8   * option) any later version.
9   *
10   * This program is distributed in the hope that it will be useful, but
11   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12   * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13   * for more details.
14   */
15  #include <linux/export.h>
16  #include <linux/kernel.h>
17  #include <linux/types.h>
18  #include <linux/errno.h>
19  #include <linux/io.h>
20  #include <linux/err.h>
21  
22  #include <video/imx-ipu-v3.h>
23  #include "ipu-prv.h"
24  
25  #define DP_SYNC 0
26  #define DP_ASYNC0 0x60
27  #define DP_ASYNC1 0xBC
28  
29  #define DP_COM_CONF		0x0
30  #define DP_GRAPH_WIND_CTRL	0x0004
31  #define DP_FG_POS		0x0008
32  #define DP_CSC_A_0		0x0044
33  #define DP_CSC_A_1		0x0048
34  #define DP_CSC_A_2		0x004C
35  #define DP_CSC_A_3		0x0050
36  #define DP_CSC_0		0x0054
37  #define DP_CSC_1		0x0058
38  
39  #define DP_COM_CONF_FG_EN		(1 << 0)
40  #define DP_COM_CONF_GWSEL		(1 << 1)
41  #define DP_COM_CONF_GWAM		(1 << 2)
42  #define DP_COM_CONF_GWCKE		(1 << 3)
43  #define DP_COM_CONF_CSC_DEF_MASK	(3 << 8)
44  #define DP_COM_CONF_CSC_DEF_OFFSET	8
45  #define DP_COM_CONF_CSC_DEF_FG		(3 << 8)
46  #define DP_COM_CONF_CSC_DEF_BG		(2 << 8)
47  #define DP_COM_CONF_CSC_DEF_BOTH	(1 << 8)
48  
49  #define IPUV3_NUM_FLOWS		3
50  
51  struct ipu_dp_priv;
52  
53  struct ipu_dp {
54  	u32 flow;
55  	bool in_use;
56  	bool foreground;
57  	enum ipu_color_space in_cs;
58  };
59  
60  struct ipu_flow {
61  	struct ipu_dp foreground;
62  	struct ipu_dp background;
63  	enum ipu_color_space out_cs;
64  	void __iomem *base;
65  	struct ipu_dp_priv *priv;
66  };
67  
68  struct ipu_dp_priv {
69  	struct ipu_soc *ipu;
70  	struct device *dev;
71  	void __iomem *base;
72  	struct ipu_flow flow[IPUV3_NUM_FLOWS];
73  	struct mutex mutex;
74  	int use_count;
75  };
76  
77  static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
78  
79  static inline struct ipu_flow *to_flow(struct ipu_dp *dp)
80  {
81  	if (dp->foreground)
82  		return container_of(dp, struct ipu_flow, foreground);
83  	else
84  		return container_of(dp, struct ipu_flow, background);
85  }
86  
87  int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
88  		u8 alpha, bool bg_chan)
89  {
90  	struct ipu_flow *flow = to_flow(dp);
91  	struct ipu_dp_priv *priv = flow->priv;
92  	u32 reg;
93  
94  	mutex_lock(&priv->mutex);
95  
96  	reg = readl(flow->base + DP_COM_CONF);
97  	if (bg_chan)
98  		reg &= ~DP_COM_CONF_GWSEL;
99  	else
100  		reg |= DP_COM_CONF_GWSEL;
101  	writel(reg, flow->base + DP_COM_CONF);
102  
103  	if (enable) {
104  		reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL;
105  		writel(reg | ((u32) alpha << 24),
106  			     flow->base + DP_GRAPH_WIND_CTRL);
107  
108  		reg = readl(flow->base + DP_COM_CONF);
109  		writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
110  	} else {
111  		reg = readl(flow->base + DP_COM_CONF);
112  		writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
113  	}
114  
115  	ipu_srm_dp_sync_update(priv->ipu);
116  
117  	mutex_unlock(&priv->mutex);
118  
119  	return 0;
120  }
121  EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
122  
123  int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
124  {
125  	struct ipu_flow *flow = to_flow(dp);
126  	struct ipu_dp_priv *priv = flow->priv;
127  
128  	writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS);
129  
130  	ipu_srm_dp_sync_update(priv->ipu);
131  
132  	return 0;
133  }
134  EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
135  
136  static void ipu_dp_csc_init(struct ipu_flow *flow,
137  		enum ipu_color_space in,
138  		enum ipu_color_space out,
139  		u32 place)
140  {
141  	u32 reg;
142  
143  	reg = readl(flow->base + DP_COM_CONF);
144  	reg &= ~DP_COM_CONF_CSC_DEF_MASK;
145  
146  	if (in == out) {
147  		writel(reg, flow->base + DP_COM_CONF);
148  		return;
149  	}
150  
151  	if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) {
152  		writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0);
153  		writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1);
154  		writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2);
155  		writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3);
156  		writel(0x3d6 | (0x0000 << 16) | (2 << 30),
157  				flow->base + DP_CSC_0);
158  		writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
159  				flow->base + DP_CSC_1);
160  	} else {
161  		writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
162  		writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1);
163  		writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2);
164  		writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3);
165  		writel(0x000 | (0x3e42 << 16) | (1 << 30),
166  				flow->base + DP_CSC_0);
167  		writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
168  				flow->base + DP_CSC_1);
169  	}
170  
171  	reg |= place;
172  
173  	writel(reg, flow->base + DP_COM_CONF);
174  }
175  
176  int ipu_dp_setup_channel(struct ipu_dp *dp,
177  		enum ipu_color_space in,
178  		enum ipu_color_space out)
179  {
180  	struct ipu_flow *flow = to_flow(dp);
181  	struct ipu_dp_priv *priv = flow->priv;
182  
183  	mutex_lock(&priv->mutex);
184  
185  	dp->in_cs = in;
186  
187  	if (!dp->foreground)
188  		flow->out_cs = out;
189  
190  	if (flow->foreground.in_cs == flow->background.in_cs) {
191  		/*
192  		 * foreground and background are of same colorspace, put
193  		 * colorspace converter after combining unit.
194  		 */
195  		ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs,
196  				DP_COM_CONF_CSC_DEF_BOTH);
197  	} else {
198  		if (flow->foreground.in_cs == flow->out_cs)
199  			/*
200  			 * foreground identical to output, apply color
201  			 * conversion on background
202  			 */
203  			ipu_dp_csc_init(flow, flow->background.in_cs,
204  					flow->out_cs, DP_COM_CONF_CSC_DEF_BG);
205  		else
206  			ipu_dp_csc_init(flow, flow->foreground.in_cs,
207  					flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
208  	}
209  
210  	ipu_srm_dp_sync_update(priv->ipu);
211  
212  	mutex_unlock(&priv->mutex);
213  
214  	return 0;
215  }
216  EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
217  
218  int ipu_dp_enable(struct ipu_soc *ipu)
219  {
220  	struct ipu_dp_priv *priv = ipu->dp_priv;
221  
222  	mutex_lock(&priv->mutex);
223  
224  	if (!priv->use_count)
225  		ipu_module_enable(priv->ipu, IPU_CONF_DP_EN);
226  
227  	priv->use_count++;
228  
229  	mutex_unlock(&priv->mutex);
230  
231  	return 0;
232  }
233  EXPORT_SYMBOL_GPL(ipu_dp_enable);
234  
235  int ipu_dp_enable_channel(struct ipu_dp *dp)
236  {
237  	struct ipu_flow *flow = to_flow(dp);
238  	struct ipu_dp_priv *priv = flow->priv;
239  	u32 reg;
240  
241  	if (!dp->foreground)
242  		return 0;
243  
244  	mutex_lock(&priv->mutex);
245  
246  	reg = readl(flow->base + DP_COM_CONF);
247  	reg |= DP_COM_CONF_FG_EN;
248  	writel(reg, flow->base + DP_COM_CONF);
249  
250  	ipu_srm_dp_sync_update(priv->ipu);
251  
252  	mutex_unlock(&priv->mutex);
253  
254  	return 0;
255  }
256  EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
257  
258  void ipu_dp_disable_channel(struct ipu_dp *dp)
259  {
260  	struct ipu_flow *flow = to_flow(dp);
261  	struct ipu_dp_priv *priv = flow->priv;
262  	u32 reg, csc;
263  
264  	if (!dp->foreground)
265  		return;
266  
267  	mutex_lock(&priv->mutex);
268  
269  	reg = readl(flow->base + DP_COM_CONF);
270  	csc = reg & DP_COM_CONF_CSC_DEF_MASK;
271  	if (csc == DP_COM_CONF_CSC_DEF_FG)
272  		reg &= ~DP_COM_CONF_CSC_DEF_MASK;
273  
274  	reg &= ~DP_COM_CONF_FG_EN;
275  	writel(reg, flow->base + DP_COM_CONF);
276  
277  	writel(0, flow->base + DP_FG_POS);
278  	ipu_srm_dp_sync_update(priv->ipu);
279  
280  	if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC))
281  		ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50);
282  
283  	mutex_unlock(&priv->mutex);
284  }
285  EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
286  
287  void ipu_dp_disable(struct ipu_soc *ipu)
288  {
289  	struct ipu_dp_priv *priv = ipu->dp_priv;
290  
291  	mutex_lock(&priv->mutex);
292  
293  	priv->use_count--;
294  
295  	if (!priv->use_count)
296  		ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
297  
298  	if (priv->use_count < 0)
299  		priv->use_count = 0;
300  
301  	mutex_unlock(&priv->mutex);
302  }
303  EXPORT_SYMBOL_GPL(ipu_dp_disable);
304  
305  struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
306  {
307  	struct ipu_dp_priv *priv = ipu->dp_priv;
308  	struct ipu_dp *dp;
309  
310  	if ((flow >> 1) >= IPUV3_NUM_FLOWS)
311  		return ERR_PTR(-EINVAL);
312  
313  	if (flow & 1)
314  		dp = &priv->flow[flow >> 1].foreground;
315  	else
316  		dp = &priv->flow[flow >> 1].background;
317  
318  	if (dp->in_use)
319  		return ERR_PTR(-EBUSY);
320  
321  	dp->in_use = true;
322  
323  	return dp;
324  }
325  EXPORT_SYMBOL_GPL(ipu_dp_get);
326  
327  void ipu_dp_put(struct ipu_dp *dp)
328  {
329  	dp->in_use = false;
330  }
331  EXPORT_SYMBOL_GPL(ipu_dp_put);
332  
333  int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base)
334  {
335  	struct ipu_dp_priv *priv;
336  	int i;
337  
338  	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
339  	if (!priv)
340  		return -ENOMEM;
341  	priv->dev = dev;
342  	priv->ipu = ipu;
343  
344  	ipu->dp_priv = priv;
345  
346  	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
347  	if (!priv->base)
348  		return -ENOMEM;
349  
350  	mutex_init(&priv->mutex);
351  
352  	for (i = 0; i < IPUV3_NUM_FLOWS; i++) {
353  		priv->flow[i].foreground.foreground = true;
354  		priv->flow[i].base = priv->base + ipu_dp_flow_base[i];
355  		priv->flow[i].priv = priv;
356  	}
357  
358  	return 0;
359  }
360  
361  void ipu_dp_exit(struct ipu_soc *ipu)
362  {
363  }
364