1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
4*4882a593Smuzhiyun * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun #include <linux/export.h>
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun #include <linux/types.h>
9*4882a593Smuzhiyun #include <linux/errno.h>
10*4882a593Smuzhiyun #include <linux/io.h>
11*4882a593Smuzhiyun #include <linux/err.h>
12*4882a593Smuzhiyun #include <linux/platform_device.h>
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <video/imx-ipu-v3.h>
15*4882a593Smuzhiyun #include "ipu-prv.h"
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun struct ipu_di {
18*4882a593Smuzhiyun void __iomem *base;
19*4882a593Smuzhiyun int id;
20*4882a593Smuzhiyun u32 module;
21*4882a593Smuzhiyun struct clk *clk_di; /* display input clock */
22*4882a593Smuzhiyun struct clk *clk_ipu; /* IPU bus clock */
23*4882a593Smuzhiyun struct clk *clk_di_pixel; /* resulting pixel clock */
24*4882a593Smuzhiyun bool inuse;
25*4882a593Smuzhiyun struct ipu_soc *ipu;
26*4882a593Smuzhiyun };
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun static DEFINE_MUTEX(di_mutex);
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun struct di_sync_config {
31*4882a593Smuzhiyun int run_count;
32*4882a593Smuzhiyun int run_src;
33*4882a593Smuzhiyun int offset_count;
34*4882a593Smuzhiyun int offset_src;
35*4882a593Smuzhiyun int repeat_count;
36*4882a593Smuzhiyun int cnt_clr_src;
37*4882a593Smuzhiyun int cnt_polarity_gen_en;
38*4882a593Smuzhiyun int cnt_polarity_clr_src;
39*4882a593Smuzhiyun int cnt_polarity_trigger_src;
40*4882a593Smuzhiyun int cnt_up;
41*4882a593Smuzhiyun int cnt_down;
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun enum di_pins {
45*4882a593Smuzhiyun DI_PIN11 = 0,
46*4882a593Smuzhiyun DI_PIN12 = 1,
47*4882a593Smuzhiyun DI_PIN13 = 2,
48*4882a593Smuzhiyun DI_PIN14 = 3,
49*4882a593Smuzhiyun DI_PIN15 = 4,
50*4882a593Smuzhiyun DI_PIN16 = 5,
51*4882a593Smuzhiyun DI_PIN17 = 6,
52*4882a593Smuzhiyun DI_PIN_CS = 7,
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun DI_PIN_SER_CLK = 0,
55*4882a593Smuzhiyun DI_PIN_SER_RS = 1,
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun enum di_sync_wave {
59*4882a593Smuzhiyun DI_SYNC_NONE = 0,
60*4882a593Smuzhiyun DI_SYNC_CLK = 1,
61*4882a593Smuzhiyun DI_SYNC_INT_HSYNC = 2,
62*4882a593Smuzhiyun DI_SYNC_HSYNC = 3,
63*4882a593Smuzhiyun DI_SYNC_VSYNC = 4,
64*4882a593Smuzhiyun DI_SYNC_DE = 6,
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun DI_SYNC_CNT1 = 2, /* counter >= 2 only */
67*4882a593Smuzhiyun DI_SYNC_CNT4 = 5, /* counter >= 5 only */
68*4882a593Smuzhiyun DI_SYNC_CNT5 = 6, /* counter >= 6 only */
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun #define SYNC_WAVE 0
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun #define DI_GENERAL 0x0000
74*4882a593Smuzhiyun #define DI_BS_CLKGEN0 0x0004
75*4882a593Smuzhiyun #define DI_BS_CLKGEN1 0x0008
76*4882a593Smuzhiyun #define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1))
77*4882a593Smuzhiyun #define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1))
78*4882a593Smuzhiyun #define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2))
79*4882a593Smuzhiyun #define DI_SYNC_AS_GEN 0x0054
80*4882a593Smuzhiyun #define DI_DW_GEN(gen) (0x0058 + 4 * (gen))
81*4882a593Smuzhiyun #define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set)))
82*4882a593Smuzhiyun #define DI_SER_CONF 0x015c
83*4882a593Smuzhiyun #define DI_SSC 0x0160
84*4882a593Smuzhiyun #define DI_POL 0x0164
85*4882a593Smuzhiyun #define DI_AW0 0x0168
86*4882a593Smuzhiyun #define DI_AW1 0x016c
87*4882a593Smuzhiyun #define DI_SCR_CONF 0x0170
88*4882a593Smuzhiyun #define DI_STAT 0x0174
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun #define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19)
91*4882a593Smuzhiyun #define DI_SW_GEN0_RUN_SRC(x) ((x) << 16)
92*4882a593Smuzhiyun #define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3)
93*4882a593Smuzhiyun #define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0)
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun #define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29)
96*4882a593Smuzhiyun #define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25)
97*4882a593Smuzhiyun #define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12)
98*4882a593Smuzhiyun #define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9)
99*4882a593Smuzhiyun #define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16)
100*4882a593Smuzhiyun #define DI_SW_GEN1_CNT_UP(x) (x)
101*4882a593Smuzhiyun #define DI_SW_GEN1_AUTO_RELOAD (0x10000000)
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun #define DI_DW_GEN_ACCESS_SIZE_OFFSET 24
104*4882a593Smuzhiyun #define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun #define DI_GEN_POLARITY_1 (1 << 0)
107*4882a593Smuzhiyun #define DI_GEN_POLARITY_2 (1 << 1)
108*4882a593Smuzhiyun #define DI_GEN_POLARITY_3 (1 << 2)
109*4882a593Smuzhiyun #define DI_GEN_POLARITY_4 (1 << 3)
110*4882a593Smuzhiyun #define DI_GEN_POLARITY_5 (1 << 4)
111*4882a593Smuzhiyun #define DI_GEN_POLARITY_6 (1 << 5)
112*4882a593Smuzhiyun #define DI_GEN_POLARITY_7 (1 << 6)
113*4882a593Smuzhiyun #define DI_GEN_POLARITY_8 (1 << 7)
114*4882a593Smuzhiyun #define DI_GEN_POLARITY_DISP_CLK (1 << 17)
115*4882a593Smuzhiyun #define DI_GEN_DI_CLK_EXT (1 << 20)
116*4882a593Smuzhiyun #define DI_GEN_DI_VSYNC_EXT (1 << 21)
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun #define DI_POL_DRDY_DATA_POLARITY (1 << 7)
119*4882a593Smuzhiyun #define DI_POL_DRDY_POLARITY_15 (1 << 4)
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun #define DI_VSYNC_SEL_OFFSET 13
122*4882a593Smuzhiyun
ipu_di_read(struct ipu_di * di,unsigned offset)123*4882a593Smuzhiyun static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun return readl(di->base + offset);
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun
ipu_di_write(struct ipu_di * di,u32 value,unsigned offset)128*4882a593Smuzhiyun static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun writel(value, di->base + offset);
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
ipu_di_data_wave_config(struct ipu_di * di,int wave_gen,int access_size,int component_size)133*4882a593Smuzhiyun static void ipu_di_data_wave_config(struct ipu_di *di,
134*4882a593Smuzhiyun int wave_gen,
135*4882a593Smuzhiyun int access_size, int component_size)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun u32 reg;
138*4882a593Smuzhiyun reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) |
139*4882a593Smuzhiyun (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET);
140*4882a593Smuzhiyun ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
ipu_di_data_pin_config(struct ipu_di * di,int wave_gen,int di_pin,int set,int up,int down)143*4882a593Smuzhiyun static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin,
144*4882a593Smuzhiyun int set, int up, int down)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun u32 reg;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun reg = ipu_di_read(di, DI_DW_GEN(wave_gen));
149*4882a593Smuzhiyun reg &= ~(0x3 << (di_pin * 2));
150*4882a593Smuzhiyun reg |= set << (di_pin * 2);
151*4882a593Smuzhiyun ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set));
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
ipu_di_sync_config(struct ipu_di * di,struct di_sync_config * config,int start,int count)156*4882a593Smuzhiyun static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config,
157*4882a593Smuzhiyun int start, int count)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun u32 reg;
160*4882a593Smuzhiyun int i;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun for (i = 0; i < count; i++) {
163*4882a593Smuzhiyun struct di_sync_config *c = &config[i];
164*4882a593Smuzhiyun int wave_gen = start + i + 1;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) ||
167*4882a593Smuzhiyun (c->repeat_count >= 0x1000) ||
168*4882a593Smuzhiyun (c->cnt_up >= 0x400) ||
169*4882a593Smuzhiyun (c->cnt_down >= 0x400)) {
170*4882a593Smuzhiyun dev_err(di->ipu->dev, "DI%d counters out of range.\n",
171*4882a593Smuzhiyun di->id);
172*4882a593Smuzhiyun return;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun reg = DI_SW_GEN0_RUN_COUNT(c->run_count) |
176*4882a593Smuzhiyun DI_SW_GEN0_RUN_SRC(c->run_src) |
177*4882a593Smuzhiyun DI_SW_GEN0_OFFSET_COUNT(c->offset_count) |
178*4882a593Smuzhiyun DI_SW_GEN0_OFFSET_SRC(c->offset_src);
179*4882a593Smuzhiyun ipu_di_write(di, reg, DI_SW_GEN0(wave_gen));
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) |
182*4882a593Smuzhiyun DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) |
183*4882a593Smuzhiyun DI_SW_GEN1_CNT_POL_TRIGGER_SRC(
184*4882a593Smuzhiyun c->cnt_polarity_trigger_src) |
185*4882a593Smuzhiyun DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) |
186*4882a593Smuzhiyun DI_SW_GEN1_CNT_DOWN(c->cnt_down) |
187*4882a593Smuzhiyun DI_SW_GEN1_CNT_UP(c->cnt_up);
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun /* Enable auto reload */
190*4882a593Smuzhiyun if (c->repeat_count == 0)
191*4882a593Smuzhiyun reg |= DI_SW_GEN1_AUTO_RELOAD;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun ipu_di_write(di, reg, DI_SW_GEN1(wave_gen));
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun reg = ipu_di_read(di, DI_STP_REP(wave_gen));
196*4882a593Smuzhiyun reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1)));
197*4882a593Smuzhiyun reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1));
198*4882a593Smuzhiyun ipu_di_write(di, reg, DI_STP_REP(wave_gen));
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
ipu_di_sync_config_interlaced(struct ipu_di * di,struct ipu_di_signal_cfg * sig)202*4882a593Smuzhiyun static void ipu_di_sync_config_interlaced(struct ipu_di *di,
203*4882a593Smuzhiyun struct ipu_di_signal_cfg *sig)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun u32 h_total = sig->mode.hactive + sig->mode.hsync_len +
206*4882a593Smuzhiyun sig->mode.hback_porch + sig->mode.hfront_porch;
207*4882a593Smuzhiyun u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
208*4882a593Smuzhiyun sig->mode.vback_porch + sig->mode.vfront_porch;
209*4882a593Smuzhiyun struct di_sync_config cfg[] = {
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun /* 1: internal VSYNC for each frame */
212*4882a593Smuzhiyun .run_count = v_total * 2 - 1,
213*4882a593Smuzhiyun .run_src = 3, /* == counter 7 */
214*4882a593Smuzhiyun }, {
215*4882a593Smuzhiyun /* PIN2: HSYNC waveform */
216*4882a593Smuzhiyun .run_count = h_total - 1,
217*4882a593Smuzhiyun .run_src = DI_SYNC_CLK,
218*4882a593Smuzhiyun .cnt_polarity_gen_en = 1,
219*4882a593Smuzhiyun .cnt_polarity_trigger_src = DI_SYNC_CLK,
220*4882a593Smuzhiyun .cnt_down = sig->mode.hsync_len * 2,
221*4882a593Smuzhiyun }, {
222*4882a593Smuzhiyun /* PIN3: VSYNC waveform */
223*4882a593Smuzhiyun .run_count = v_total - 1,
224*4882a593Smuzhiyun .run_src = 4, /* == counter 7 */
225*4882a593Smuzhiyun .cnt_polarity_gen_en = 1,
226*4882a593Smuzhiyun .cnt_polarity_trigger_src = 4, /* == counter 7 */
227*4882a593Smuzhiyun .cnt_down = sig->mode.vsync_len * 2,
228*4882a593Smuzhiyun .cnt_clr_src = DI_SYNC_CNT1,
229*4882a593Smuzhiyun }, {
230*4882a593Smuzhiyun /* 4: Field */
231*4882a593Smuzhiyun .run_count = v_total / 2,
232*4882a593Smuzhiyun .run_src = DI_SYNC_HSYNC,
233*4882a593Smuzhiyun .offset_count = h_total / 2,
234*4882a593Smuzhiyun .offset_src = DI_SYNC_CLK,
235*4882a593Smuzhiyun .repeat_count = 2,
236*4882a593Smuzhiyun .cnt_clr_src = DI_SYNC_CNT1,
237*4882a593Smuzhiyun }, {
238*4882a593Smuzhiyun /* 5: Active lines */
239*4882a593Smuzhiyun .run_src = DI_SYNC_HSYNC,
240*4882a593Smuzhiyun .offset_count = (sig->mode.vsync_len +
241*4882a593Smuzhiyun sig->mode.vback_porch) / 2,
242*4882a593Smuzhiyun .offset_src = DI_SYNC_HSYNC,
243*4882a593Smuzhiyun .repeat_count = sig->mode.vactive / 2,
244*4882a593Smuzhiyun .cnt_clr_src = DI_SYNC_CNT4,
245*4882a593Smuzhiyun }, {
246*4882a593Smuzhiyun /* 6: Active pixel, referenced by DC */
247*4882a593Smuzhiyun .run_src = DI_SYNC_CLK,
248*4882a593Smuzhiyun .offset_count = sig->mode.hsync_len +
249*4882a593Smuzhiyun sig->mode.hback_porch,
250*4882a593Smuzhiyun .offset_src = DI_SYNC_CLK,
251*4882a593Smuzhiyun .repeat_count = sig->mode.hactive,
252*4882a593Smuzhiyun .cnt_clr_src = DI_SYNC_CNT5,
253*4882a593Smuzhiyun }, {
254*4882a593Smuzhiyun /* 7: Half line HSYNC */
255*4882a593Smuzhiyun .run_count = h_total / 2 - 1,
256*4882a593Smuzhiyun .run_src = DI_SYNC_CLK,
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun };
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun
ipu_di_sync_config_noninterlaced(struct ipu_di * di,struct ipu_di_signal_cfg * sig,int div)265*4882a593Smuzhiyun static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
266*4882a593Smuzhiyun struct ipu_di_signal_cfg *sig, int div)
267*4882a593Smuzhiyun {
268*4882a593Smuzhiyun u32 h_total = sig->mode.hactive + sig->mode.hsync_len +
269*4882a593Smuzhiyun sig->mode.hback_porch + sig->mode.hfront_porch;
270*4882a593Smuzhiyun u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
271*4882a593Smuzhiyun sig->mode.vback_porch + sig->mode.vfront_porch;
272*4882a593Smuzhiyun struct di_sync_config cfg[] = {
273*4882a593Smuzhiyun {
274*4882a593Smuzhiyun /* 1: INT_HSYNC */
275*4882a593Smuzhiyun .run_count = h_total - 1,
276*4882a593Smuzhiyun .run_src = DI_SYNC_CLK,
277*4882a593Smuzhiyun } , {
278*4882a593Smuzhiyun /* PIN2: HSYNC */
279*4882a593Smuzhiyun .run_count = h_total - 1,
280*4882a593Smuzhiyun .run_src = DI_SYNC_CLK,
281*4882a593Smuzhiyun .offset_count = div * sig->v_to_h_sync,
282*4882a593Smuzhiyun .offset_src = DI_SYNC_CLK,
283*4882a593Smuzhiyun .cnt_polarity_gen_en = 1,
284*4882a593Smuzhiyun .cnt_polarity_trigger_src = DI_SYNC_CLK,
285*4882a593Smuzhiyun .cnt_down = sig->mode.hsync_len * 2,
286*4882a593Smuzhiyun } , {
287*4882a593Smuzhiyun /* PIN3: VSYNC */
288*4882a593Smuzhiyun .run_count = v_total - 1,
289*4882a593Smuzhiyun .run_src = DI_SYNC_INT_HSYNC,
290*4882a593Smuzhiyun .cnt_polarity_gen_en = 1,
291*4882a593Smuzhiyun .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
292*4882a593Smuzhiyun .cnt_down = sig->mode.vsync_len * 2,
293*4882a593Smuzhiyun } , {
294*4882a593Smuzhiyun /* 4: Line Active */
295*4882a593Smuzhiyun .run_src = DI_SYNC_HSYNC,
296*4882a593Smuzhiyun .offset_count = sig->mode.vsync_len +
297*4882a593Smuzhiyun sig->mode.vback_porch,
298*4882a593Smuzhiyun .offset_src = DI_SYNC_HSYNC,
299*4882a593Smuzhiyun .repeat_count = sig->mode.vactive,
300*4882a593Smuzhiyun .cnt_clr_src = DI_SYNC_VSYNC,
301*4882a593Smuzhiyun } , {
302*4882a593Smuzhiyun /* 5: Pixel Active, referenced by DC */
303*4882a593Smuzhiyun .run_src = DI_SYNC_CLK,
304*4882a593Smuzhiyun .offset_count = sig->mode.hsync_len +
305*4882a593Smuzhiyun sig->mode.hback_porch,
306*4882a593Smuzhiyun .offset_src = DI_SYNC_CLK,
307*4882a593Smuzhiyun .repeat_count = sig->mode.hactive,
308*4882a593Smuzhiyun .cnt_clr_src = 5, /* Line Active */
309*4882a593Smuzhiyun } , {
310*4882a593Smuzhiyun /* unused */
311*4882a593Smuzhiyun } , {
312*4882a593Smuzhiyun /* unused */
313*4882a593Smuzhiyun } , {
314*4882a593Smuzhiyun /* unused */
315*4882a593Smuzhiyun } , {
316*4882a593Smuzhiyun /* unused */
317*4882a593Smuzhiyun },
318*4882a593Smuzhiyun };
319*4882a593Smuzhiyun /* can't use #7 and #8 for line active and pixel active counters */
320*4882a593Smuzhiyun struct di_sync_config cfg_vga[] = {
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun /* 1: INT_HSYNC */
323*4882a593Smuzhiyun .run_count = h_total - 1,
324*4882a593Smuzhiyun .run_src = DI_SYNC_CLK,
325*4882a593Smuzhiyun } , {
326*4882a593Smuzhiyun /* 2: VSYNC */
327*4882a593Smuzhiyun .run_count = v_total - 1,
328*4882a593Smuzhiyun .run_src = DI_SYNC_INT_HSYNC,
329*4882a593Smuzhiyun } , {
330*4882a593Smuzhiyun /* 3: Line Active */
331*4882a593Smuzhiyun .run_src = DI_SYNC_INT_HSYNC,
332*4882a593Smuzhiyun .offset_count = sig->mode.vsync_len +
333*4882a593Smuzhiyun sig->mode.vback_porch,
334*4882a593Smuzhiyun .offset_src = DI_SYNC_INT_HSYNC,
335*4882a593Smuzhiyun .repeat_count = sig->mode.vactive,
336*4882a593Smuzhiyun .cnt_clr_src = 3 /* VSYNC */,
337*4882a593Smuzhiyun } , {
338*4882a593Smuzhiyun /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */
339*4882a593Smuzhiyun .run_count = h_total - 1,
340*4882a593Smuzhiyun .run_src = DI_SYNC_CLK,
341*4882a593Smuzhiyun .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */
342*4882a593Smuzhiyun .offset_src = DI_SYNC_CLK,
343*4882a593Smuzhiyun .cnt_polarity_gen_en = 1,
344*4882a593Smuzhiyun .cnt_polarity_trigger_src = DI_SYNC_CLK,
345*4882a593Smuzhiyun .cnt_down = sig->mode.hsync_len * 2,
346*4882a593Smuzhiyun } , {
347*4882a593Smuzhiyun /* 5: Pixel Active signal to DC */
348*4882a593Smuzhiyun .run_src = DI_SYNC_CLK,
349*4882a593Smuzhiyun .offset_count = sig->mode.hsync_len +
350*4882a593Smuzhiyun sig->mode.hback_porch,
351*4882a593Smuzhiyun .offset_src = DI_SYNC_CLK,
352*4882a593Smuzhiyun .repeat_count = sig->mode.hactive,
353*4882a593Smuzhiyun .cnt_clr_src = 4, /* Line Active */
354*4882a593Smuzhiyun } , {
355*4882a593Smuzhiyun /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */
356*4882a593Smuzhiyun .run_count = v_total - 1,
357*4882a593Smuzhiyun .run_src = DI_SYNC_INT_HSYNC,
358*4882a593Smuzhiyun .offset_count = 1, /* magic value from Freescale TVE driver */
359*4882a593Smuzhiyun .offset_src = DI_SYNC_INT_HSYNC,
360*4882a593Smuzhiyun .cnt_polarity_gen_en = 1,
361*4882a593Smuzhiyun .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
362*4882a593Smuzhiyun .cnt_down = sig->mode.vsync_len * 2,
363*4882a593Smuzhiyun } , {
364*4882a593Smuzhiyun /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */
365*4882a593Smuzhiyun .run_count = h_total - 1,
366*4882a593Smuzhiyun .run_src = DI_SYNC_CLK,
367*4882a593Smuzhiyun .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */
368*4882a593Smuzhiyun .offset_src = DI_SYNC_CLK,
369*4882a593Smuzhiyun .cnt_polarity_gen_en = 1,
370*4882a593Smuzhiyun .cnt_polarity_trigger_src = DI_SYNC_CLK,
371*4882a593Smuzhiyun .cnt_down = sig->mode.hsync_len * 2,
372*4882a593Smuzhiyun } , {
373*4882a593Smuzhiyun /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */
374*4882a593Smuzhiyun .run_count = v_total - 1,
375*4882a593Smuzhiyun .run_src = DI_SYNC_INT_HSYNC,
376*4882a593Smuzhiyun .offset_count = 1, /* magic value from Freescale TVE driver */
377*4882a593Smuzhiyun .offset_src = DI_SYNC_INT_HSYNC,
378*4882a593Smuzhiyun .cnt_polarity_gen_en = 1,
379*4882a593Smuzhiyun .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
380*4882a593Smuzhiyun .cnt_down = sig->mode.vsync_len * 2,
381*4882a593Smuzhiyun } , {
382*4882a593Smuzhiyun /* unused */
383*4882a593Smuzhiyun },
384*4882a593Smuzhiyun };
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun ipu_di_write(di, v_total - 1, DI_SCR_CONF);
387*4882a593Smuzhiyun if (sig->hsync_pin == 2 && sig->vsync_pin == 3)
388*4882a593Smuzhiyun ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
389*4882a593Smuzhiyun else
390*4882a593Smuzhiyun ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga));
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun
ipu_di_config_clock(struct ipu_di * di,const struct ipu_di_signal_cfg * sig)393*4882a593Smuzhiyun static void ipu_di_config_clock(struct ipu_di *di,
394*4882a593Smuzhiyun const struct ipu_di_signal_cfg *sig)
395*4882a593Smuzhiyun {
396*4882a593Smuzhiyun struct clk *clk;
397*4882a593Smuzhiyun unsigned clkgen0;
398*4882a593Smuzhiyun uint32_t val;
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun if (sig->clkflags & IPU_DI_CLKMODE_EXT) {
401*4882a593Smuzhiyun /*
402*4882a593Smuzhiyun * CLKMODE_EXT means we must use the DI clock: this is
403*4882a593Smuzhiyun * needed for things like LVDS which needs to feed the
404*4882a593Smuzhiyun * DI and LDB with the same pixel clock.
405*4882a593Smuzhiyun */
406*4882a593Smuzhiyun clk = di->clk_di;
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun if (sig->clkflags & IPU_DI_CLKMODE_SYNC) {
409*4882a593Smuzhiyun /*
410*4882a593Smuzhiyun * CLKMODE_SYNC means that we want the DI to be
411*4882a593Smuzhiyun * clocked at the same rate as the parent clock.
412*4882a593Smuzhiyun * This is needed (eg) for LDB which needs to be
413*4882a593Smuzhiyun * fed with the same pixel clock. We assume that
414*4882a593Smuzhiyun * the LDB clock has already been set correctly.
415*4882a593Smuzhiyun */
416*4882a593Smuzhiyun clkgen0 = 1 << 4;
417*4882a593Smuzhiyun } else {
418*4882a593Smuzhiyun /*
419*4882a593Smuzhiyun * We can use the divider. We should really have
420*4882a593Smuzhiyun * a flag here indicating whether the bridge can
421*4882a593Smuzhiyun * cope with a fractional divider or not. For the
422*4882a593Smuzhiyun * time being, let's go for simplicitly and
423*4882a593Smuzhiyun * reliability.
424*4882a593Smuzhiyun */
425*4882a593Smuzhiyun unsigned long in_rate;
426*4882a593Smuzhiyun unsigned div;
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun clk_set_rate(clk, sig->mode.pixelclock);
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun in_rate = clk_get_rate(clk);
431*4882a593Smuzhiyun div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock);
432*4882a593Smuzhiyun div = clamp(div, 1U, 255U);
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun clkgen0 = div << 4;
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun } else {
437*4882a593Smuzhiyun /*
438*4882a593Smuzhiyun * For other interfaces, we can arbitarily select between
439*4882a593Smuzhiyun * the DI specific clock and the internal IPU clock. See
440*4882a593Smuzhiyun * DI_GENERAL bit 20. We select the IPU clock if it can
441*4882a593Smuzhiyun * give us a clock rate within 1% of the requested frequency,
442*4882a593Smuzhiyun * otherwise we use the DI clock.
443*4882a593Smuzhiyun */
444*4882a593Smuzhiyun unsigned long rate, clkrate;
445*4882a593Smuzhiyun unsigned div, error;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun clkrate = clk_get_rate(di->clk_ipu);
448*4882a593Smuzhiyun div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock);
449*4882a593Smuzhiyun div = clamp(div, 1U, 255U);
450*4882a593Smuzhiyun rate = clkrate / div;
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun error = rate / (sig->mode.pixelclock / 1000);
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %c%d.%d%%\n",
455*4882a593Smuzhiyun rate, div, error < 1000 ? '-' : '+',
456*4882a593Smuzhiyun abs(error - 1000) / 10, abs(error - 1000) % 10);
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun /* Allow a 1% error */
459*4882a593Smuzhiyun if (error < 1010 && error >= 990) {
460*4882a593Smuzhiyun clk = di->clk_ipu;
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun clkgen0 = div << 4;
463*4882a593Smuzhiyun } else {
464*4882a593Smuzhiyun unsigned long in_rate;
465*4882a593Smuzhiyun unsigned div;
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun clk = di->clk_di;
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun clk_set_rate(clk, sig->mode.pixelclock);
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun in_rate = clk_get_rate(clk);
472*4882a593Smuzhiyun div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock);
473*4882a593Smuzhiyun div = clamp(div, 1U, 255U);
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun clkgen0 = div << 4;
476*4882a593Smuzhiyun }
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun di->clk_di_pixel = clk;
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun /* Set the divider */
482*4882a593Smuzhiyun ipu_di_write(di, clkgen0, DI_BS_CLKGEN0);
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun /*
485*4882a593Smuzhiyun * Set the high/low periods. Bits 24:16 give us the falling edge,
486*4882a593Smuzhiyun * and bits 8:0 give the rising edge. LSB is fraction, and is
487*4882a593Smuzhiyun * based on the divider above. We want a 50% duty cycle, so set
488*4882a593Smuzhiyun * the falling edge to be half the divider.
489*4882a593Smuzhiyun */
490*4882a593Smuzhiyun ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1);
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun /* Finally select the input clock */
493*4882a593Smuzhiyun val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT;
494*4882a593Smuzhiyun if (clk == di->clk_di)
495*4882a593Smuzhiyun val |= DI_GEN_DI_CLK_EXT;
496*4882a593Smuzhiyun ipu_di_write(di, val, DI_GENERAL);
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n",
499*4882a593Smuzhiyun sig->mode.pixelclock,
500*4882a593Smuzhiyun clk_get_rate(di->clk_ipu),
501*4882a593Smuzhiyun clk_get_rate(di->clk_di),
502*4882a593Smuzhiyun clk == di->clk_di ? "DI" : "IPU",
503*4882a593Smuzhiyun clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4));
504*4882a593Smuzhiyun }
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun /*
507*4882a593Smuzhiyun * This function is called to adjust a video mode to IPU restrictions.
508*4882a593Smuzhiyun * It is meant to be called from drm crtc mode_fixup() methods.
509*4882a593Smuzhiyun */
ipu_di_adjust_videomode(struct ipu_di * di,struct videomode * mode)510*4882a593Smuzhiyun int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun u32 diff;
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun if (mode->vfront_porch >= 2)
515*4882a593Smuzhiyun return 0;
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun diff = 2 - mode->vfront_porch;
518*4882a593Smuzhiyun
519*4882a593Smuzhiyun if (mode->vback_porch >= diff) {
520*4882a593Smuzhiyun mode->vfront_porch = 2;
521*4882a593Smuzhiyun mode->vback_porch -= diff;
522*4882a593Smuzhiyun } else if (mode->vsync_len > diff) {
523*4882a593Smuzhiyun mode->vfront_porch = 2;
524*4882a593Smuzhiyun mode->vsync_len = mode->vsync_len - diff;
525*4882a593Smuzhiyun } else {
526*4882a593Smuzhiyun dev_warn(di->ipu->dev, "failed to adjust videomode\n");
527*4882a593Smuzhiyun return -EINVAL;
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun dev_dbg(di->ipu->dev, "videomode adapted for IPU restrictions\n");
531*4882a593Smuzhiyun return 0;
532*4882a593Smuzhiyun }
533*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode);
534*4882a593Smuzhiyun
ipu_di_gen_polarity(int pin)535*4882a593Smuzhiyun static u32 ipu_di_gen_polarity(int pin)
536*4882a593Smuzhiyun {
537*4882a593Smuzhiyun switch (pin) {
538*4882a593Smuzhiyun case 1:
539*4882a593Smuzhiyun return DI_GEN_POLARITY_1;
540*4882a593Smuzhiyun case 2:
541*4882a593Smuzhiyun return DI_GEN_POLARITY_2;
542*4882a593Smuzhiyun case 3:
543*4882a593Smuzhiyun return DI_GEN_POLARITY_3;
544*4882a593Smuzhiyun case 4:
545*4882a593Smuzhiyun return DI_GEN_POLARITY_4;
546*4882a593Smuzhiyun case 5:
547*4882a593Smuzhiyun return DI_GEN_POLARITY_5;
548*4882a593Smuzhiyun case 6:
549*4882a593Smuzhiyun return DI_GEN_POLARITY_6;
550*4882a593Smuzhiyun case 7:
551*4882a593Smuzhiyun return DI_GEN_POLARITY_7;
552*4882a593Smuzhiyun case 8:
553*4882a593Smuzhiyun return DI_GEN_POLARITY_8;
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun return 0;
556*4882a593Smuzhiyun }
557*4882a593Smuzhiyun
ipu_di_init_sync_panel(struct ipu_di * di,struct ipu_di_signal_cfg * sig)558*4882a593Smuzhiyun int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
559*4882a593Smuzhiyun {
560*4882a593Smuzhiyun u32 reg;
561*4882a593Smuzhiyun u32 di_gen, vsync_cnt;
562*4882a593Smuzhiyun u32 div;
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n",
565*4882a593Smuzhiyun di->id, sig->mode.hactive, sig->mode.vactive);
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n",
568*4882a593Smuzhiyun clk_get_rate(di->clk_ipu),
569*4882a593Smuzhiyun clk_get_rate(di->clk_di),
570*4882a593Smuzhiyun sig->mode.pixelclock);
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun mutex_lock(&di_mutex);
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun ipu_di_config_clock(di, sig);
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff;
577*4882a593Smuzhiyun div = div / 16; /* Now divider is integer portion */
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun /* Setup pixel clock timing */
580*4882a593Smuzhiyun /* Down time is half of period */
581*4882a593Smuzhiyun ipu_di_write(di, (div << 16), DI_BS_CLKGEN1);
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1);
584*4882a593Smuzhiyun ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2);
585*4882a593Smuzhiyun
586*4882a593Smuzhiyun di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT;
587*4882a593Smuzhiyun di_gen |= DI_GEN_DI_VSYNC_EXT;
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) {
590*4882a593Smuzhiyun ipu_di_sync_config_interlaced(di, sig);
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun /* set y_sel = 1 */
593*4882a593Smuzhiyun di_gen |= 0x10000000;
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun vsync_cnt = 3;
596*4882a593Smuzhiyun } else {
597*4882a593Smuzhiyun ipu_di_sync_config_noninterlaced(di, sig, div);
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun vsync_cnt = 3;
600*4882a593Smuzhiyun if (di->id == 1)
601*4882a593Smuzhiyun /*
602*4882a593Smuzhiyun * TODO: change only for TVEv2, parallel display
603*4882a593Smuzhiyun * uses pin 2 / 3
604*4882a593Smuzhiyun */
605*4882a593Smuzhiyun if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3))
606*4882a593Smuzhiyun vsync_cnt = 6;
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH)
610*4882a593Smuzhiyun di_gen |= ipu_di_gen_polarity(sig->hsync_pin);
611*4882a593Smuzhiyun if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH)
612*4882a593Smuzhiyun di_gen |= ipu_di_gen_polarity(sig->vsync_pin);
613*4882a593Smuzhiyun
614*4882a593Smuzhiyun if (sig->clk_pol)
615*4882a593Smuzhiyun di_gen |= DI_GEN_POLARITY_DISP_CLK;
616*4882a593Smuzhiyun
617*4882a593Smuzhiyun ipu_di_write(di, di_gen, DI_GENERAL);
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,
620*4882a593Smuzhiyun DI_SYNC_AS_GEN);
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun reg = ipu_di_read(di, DI_POL);
623*4882a593Smuzhiyun reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15);
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun if (sig->enable_pol)
626*4882a593Smuzhiyun reg |= DI_POL_DRDY_POLARITY_15;
627*4882a593Smuzhiyun if (sig->data_pol)
628*4882a593Smuzhiyun reg |= DI_POL_DRDY_DATA_POLARITY;
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun ipu_di_write(di, reg, DI_POL);
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun mutex_unlock(&di_mutex);
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun return 0;
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);
637*4882a593Smuzhiyun
ipu_di_enable(struct ipu_di * di)638*4882a593Smuzhiyun int ipu_di_enable(struct ipu_di *di)
639*4882a593Smuzhiyun {
640*4882a593Smuzhiyun int ret;
641*4882a593Smuzhiyun
642*4882a593Smuzhiyun WARN_ON(IS_ERR(di->clk_di_pixel));
643*4882a593Smuzhiyun
644*4882a593Smuzhiyun ret = clk_prepare_enable(di->clk_di_pixel);
645*4882a593Smuzhiyun if (ret)
646*4882a593Smuzhiyun return ret;
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun ipu_module_enable(di->ipu, di->module);
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun return 0;
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipu_di_enable);
653*4882a593Smuzhiyun
ipu_di_disable(struct ipu_di * di)654*4882a593Smuzhiyun int ipu_di_disable(struct ipu_di *di)
655*4882a593Smuzhiyun {
656*4882a593Smuzhiyun WARN_ON(IS_ERR(di->clk_di_pixel));
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun ipu_module_disable(di->ipu, di->module);
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun clk_disable_unprepare(di->clk_di_pixel);
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun return 0;
663*4882a593Smuzhiyun }
664*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipu_di_disable);
665*4882a593Smuzhiyun
ipu_di_get_num(struct ipu_di * di)666*4882a593Smuzhiyun int ipu_di_get_num(struct ipu_di *di)
667*4882a593Smuzhiyun {
668*4882a593Smuzhiyun return di->id;
669*4882a593Smuzhiyun }
670*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipu_di_get_num);
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun static DEFINE_MUTEX(ipu_di_lock);
673*4882a593Smuzhiyun
ipu_di_get(struct ipu_soc * ipu,int disp)674*4882a593Smuzhiyun struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp)
675*4882a593Smuzhiyun {
676*4882a593Smuzhiyun struct ipu_di *di;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun if (disp > 1)
679*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
680*4882a593Smuzhiyun
681*4882a593Smuzhiyun di = ipu->di_priv[disp];
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun mutex_lock(&ipu_di_lock);
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun if (di->inuse) {
686*4882a593Smuzhiyun di = ERR_PTR(-EBUSY);
687*4882a593Smuzhiyun goto out;
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun
690*4882a593Smuzhiyun di->inuse = true;
691*4882a593Smuzhiyun out:
692*4882a593Smuzhiyun mutex_unlock(&ipu_di_lock);
693*4882a593Smuzhiyun
694*4882a593Smuzhiyun return di;
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipu_di_get);
697*4882a593Smuzhiyun
ipu_di_put(struct ipu_di * di)698*4882a593Smuzhiyun void ipu_di_put(struct ipu_di *di)
699*4882a593Smuzhiyun {
700*4882a593Smuzhiyun mutex_lock(&ipu_di_lock);
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun di->inuse = false;
703*4882a593Smuzhiyun
704*4882a593Smuzhiyun mutex_unlock(&ipu_di_lock);
705*4882a593Smuzhiyun }
706*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipu_di_put);
707*4882a593Smuzhiyun
ipu_di_init(struct ipu_soc * ipu,struct device * dev,int id,unsigned long base,u32 module,struct clk * clk_ipu)708*4882a593Smuzhiyun int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
709*4882a593Smuzhiyun unsigned long base,
710*4882a593Smuzhiyun u32 module, struct clk *clk_ipu)
711*4882a593Smuzhiyun {
712*4882a593Smuzhiyun struct ipu_di *di;
713*4882a593Smuzhiyun
714*4882a593Smuzhiyun if (id > 1)
715*4882a593Smuzhiyun return -ENODEV;
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
718*4882a593Smuzhiyun if (!di)
719*4882a593Smuzhiyun return -ENOMEM;
720*4882a593Smuzhiyun
721*4882a593Smuzhiyun ipu->di_priv[id] = di;
722*4882a593Smuzhiyun
723*4882a593Smuzhiyun di->clk_di = devm_clk_get(dev, id ? "di1" : "di0");
724*4882a593Smuzhiyun if (IS_ERR(di->clk_di))
725*4882a593Smuzhiyun return PTR_ERR(di->clk_di);
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun di->module = module;
728*4882a593Smuzhiyun di->id = id;
729*4882a593Smuzhiyun di->clk_ipu = clk_ipu;
730*4882a593Smuzhiyun di->base = devm_ioremap(dev, base, PAGE_SIZE);
731*4882a593Smuzhiyun if (!di->base)
732*4882a593Smuzhiyun return -ENOMEM;
733*4882a593Smuzhiyun
734*4882a593Smuzhiyun ipu_di_write(di, 0x10, DI_BS_CLKGEN0);
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n",
737*4882a593Smuzhiyun id, base, di->base);
738*4882a593Smuzhiyun di->inuse = false;
739*4882a593Smuzhiyun di->ipu = ipu;
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun return 0;
742*4882a593Smuzhiyun }
743*4882a593Smuzhiyun
ipu_di_exit(struct ipu_soc * ipu,int id)744*4882a593Smuzhiyun void ipu_di_exit(struct ipu_soc *ipu, int id)
745*4882a593Smuzhiyun {
746*4882a593Smuzhiyun }
747