xref: /openbmc/linux/drivers/gpu/ipu-v3/ipu-vdi.c (revision 82e6fdd6)
1 /*
2  * Copyright (C) 2012-2016 Mentor Graphics Inc.
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/io.h>
16 #include "ipu-prv.h"
17 
18 struct ipu_vdi {
19 	void __iomem *base;
20 	u32 module;
21 	spinlock_t lock;
22 	int use_count;
23 	struct ipu_soc *ipu;
24 };
25 
26 
27 /* VDI Register Offsets */
28 #define VDI_FSIZE 0x0000
29 #define VDI_C     0x0004
30 
31 /* VDI Register Fields */
32 #define VDI_C_CH_420             (0 << 1)
33 #define VDI_C_CH_422             (1 << 1)
34 #define VDI_C_MOT_SEL_MASK       (0x3 << 2)
35 #define VDI_C_MOT_SEL_FULL       (2 << 2)
36 #define VDI_C_MOT_SEL_LOW        (1 << 2)
37 #define VDI_C_MOT_SEL_MED        (0 << 2)
38 #define VDI_C_BURST_SIZE1_4      (3 << 4)
39 #define VDI_C_BURST_SIZE2_4      (3 << 8)
40 #define VDI_C_BURST_SIZE3_4      (3 << 12)
41 #define VDI_C_BURST_SIZE_MASK    0xF
42 #define VDI_C_BURST_SIZE1_OFFSET 4
43 #define VDI_C_BURST_SIZE2_OFFSET 8
44 #define VDI_C_BURST_SIZE3_OFFSET 12
45 #define VDI_C_VWM1_SET_1         (0 << 16)
46 #define VDI_C_VWM1_SET_2         (1 << 16)
47 #define VDI_C_VWM1_CLR_2         (1 << 19)
48 #define VDI_C_VWM3_SET_1         (0 << 22)
49 #define VDI_C_VWM3_SET_2         (1 << 22)
50 #define VDI_C_VWM3_CLR_2         (1 << 25)
51 #define VDI_C_TOP_FIELD_MAN_1    (1 << 30)
52 #define VDI_C_TOP_FIELD_AUTO_1   (1 << 31)
53 
54 static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset)
55 {
56 	return readl(vdi->base + offset);
57 }
58 
59 static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value,
60 				 unsigned int offset)
61 {
62 	writel(value, vdi->base + offset);
63 }
64 
65 void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field)
66 {
67 	bool top_field_0 = false;
68 	unsigned long flags;
69 	u32 reg;
70 
71 	switch (field) {
72 	case V4L2_FIELD_INTERLACED_TB:
73 	case V4L2_FIELD_SEQ_TB:
74 	case V4L2_FIELD_TOP:
75 		top_field_0 = true;
76 		break;
77 	case V4L2_FIELD_INTERLACED_BT:
78 	case V4L2_FIELD_SEQ_BT:
79 	case V4L2_FIELD_BOTTOM:
80 		top_field_0 = false;
81 		break;
82 	default:
83 		top_field_0 = (std & V4L2_STD_525_60) ? true : false;
84 		break;
85 	}
86 
87 	spin_lock_irqsave(&vdi->lock, flags);
88 
89 	reg = ipu_vdi_read(vdi, VDI_C);
90 	if (top_field_0)
91 		reg &= ~(VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1);
92 	else
93 		reg |= VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1;
94 	ipu_vdi_write(vdi, reg, VDI_C);
95 
96 	spin_unlock_irqrestore(&vdi->lock, flags);
97 }
98 EXPORT_SYMBOL_GPL(ipu_vdi_set_field_order);
99 
100 void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel)
101 {
102 	unsigned long flags;
103 	u32 reg;
104 
105 	spin_lock_irqsave(&vdi->lock, flags);
106 
107 	reg = ipu_vdi_read(vdi, VDI_C);
108 
109 	reg &= ~VDI_C_MOT_SEL_MASK;
110 
111 	switch (motion_sel) {
112 	case MED_MOTION:
113 		reg |= VDI_C_MOT_SEL_MED;
114 		break;
115 	case HIGH_MOTION:
116 		reg |= VDI_C_MOT_SEL_FULL;
117 		break;
118 	default:
119 		reg |= VDI_C_MOT_SEL_LOW;
120 		break;
121 	}
122 
123 	ipu_vdi_write(vdi, reg, VDI_C);
124 
125 	spin_unlock_irqrestore(&vdi->lock, flags);
126 }
127 EXPORT_SYMBOL_GPL(ipu_vdi_set_motion);
128 
129 void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres)
130 {
131 	unsigned long flags;
132 	u32 pixel_fmt, reg;
133 
134 	spin_lock_irqsave(&vdi->lock, flags);
135 
136 	reg = ((yres - 1) << 16) | (xres - 1);
137 	ipu_vdi_write(vdi, reg, VDI_FSIZE);
138 
139 	/*
140 	 * Full motion, only vertical filter is used.
141 	 * Burst size is 4 accesses
142 	 */
143 	if (code == MEDIA_BUS_FMT_UYVY8_2X8 ||
144 	    code == MEDIA_BUS_FMT_UYVY8_1X16 ||
145 	    code == MEDIA_BUS_FMT_YUYV8_2X8 ||
146 	    code == MEDIA_BUS_FMT_YUYV8_1X16)
147 		pixel_fmt = VDI_C_CH_422;
148 	else
149 		pixel_fmt = VDI_C_CH_420;
150 
151 	reg = ipu_vdi_read(vdi, VDI_C);
152 	reg |= pixel_fmt;
153 	reg |= VDI_C_BURST_SIZE2_4;
154 	reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2;
155 	reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2;
156 	ipu_vdi_write(vdi, reg, VDI_C);
157 
158 	spin_unlock_irqrestore(&vdi->lock, flags);
159 }
160 EXPORT_SYMBOL_GPL(ipu_vdi_setup);
161 
162 void ipu_vdi_unsetup(struct ipu_vdi *vdi)
163 {
164 	unsigned long flags;
165 
166 	spin_lock_irqsave(&vdi->lock, flags);
167 	ipu_vdi_write(vdi, 0, VDI_FSIZE);
168 	ipu_vdi_write(vdi, 0, VDI_C);
169 	spin_unlock_irqrestore(&vdi->lock, flags);
170 }
171 EXPORT_SYMBOL_GPL(ipu_vdi_unsetup);
172 
173 int ipu_vdi_enable(struct ipu_vdi *vdi)
174 {
175 	unsigned long flags;
176 
177 	spin_lock_irqsave(&vdi->lock, flags);
178 
179 	if (!vdi->use_count)
180 		ipu_module_enable(vdi->ipu, vdi->module);
181 
182 	vdi->use_count++;
183 
184 	spin_unlock_irqrestore(&vdi->lock, flags);
185 
186 	return 0;
187 }
188 EXPORT_SYMBOL_GPL(ipu_vdi_enable);
189 
190 int ipu_vdi_disable(struct ipu_vdi *vdi)
191 {
192 	unsigned long flags;
193 
194 	spin_lock_irqsave(&vdi->lock, flags);
195 
196 	if (vdi->use_count) {
197 		if (!--vdi->use_count)
198 			ipu_module_disable(vdi->ipu, vdi->module);
199 	}
200 
201 	spin_unlock_irqrestore(&vdi->lock, flags);
202 
203 	return 0;
204 }
205 EXPORT_SYMBOL_GPL(ipu_vdi_disable);
206 
207 struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu)
208 {
209 	return ipu->vdi_priv;
210 }
211 EXPORT_SYMBOL_GPL(ipu_vdi_get);
212 
213 void ipu_vdi_put(struct ipu_vdi *vdi)
214 {
215 }
216 EXPORT_SYMBOL_GPL(ipu_vdi_put);
217 
218 int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
219 		 unsigned long base, u32 module)
220 {
221 	struct ipu_vdi *vdi;
222 
223 	vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL);
224 	if (!vdi)
225 		return -ENOMEM;
226 
227 	ipu->vdi_priv = vdi;
228 
229 	spin_lock_init(&vdi->lock);
230 	vdi->module = module;
231 	vdi->base = devm_ioremap(dev, base, PAGE_SIZE);
232 	if (!vdi->base)
233 		return -ENOMEM;
234 
235 	dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base);
236 	vdi->ipu = ipu;
237 
238 	return 0;
239 }
240 
241 void ipu_vdi_exit(struct ipu_soc *ipu)
242 {
243 }
244