1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> |
4 | * |
5 | * Original author: |
6 | * Ben Collins <bcollins@ubuntu.com> |
7 | * |
8 | * Additional work by: |
9 | * John Brooks <john.brooks@bluecherry.net> |
10 | */ |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/font.h> |
14 | #include <linux/bitrev.h> |
15 | #include <linux/slab.h> |
16 | |
17 | #include "solo6x10.h" |
18 | |
19 | #define VI_PROG_HSIZE (1280 - 16) |
20 | #define VI_PROG_VSIZE (1024 - 16) |
21 | |
22 | #define IRQ_LEVEL 2 |
23 | |
24 | static void solo_capture_config(struct solo_dev *solo_dev) |
25 | { |
26 | unsigned long height; |
27 | unsigned long width; |
28 | void *buf; |
29 | int i; |
30 | |
31 | solo_reg_write(solo_dev, SOLO_CAP_BASE, |
32 | SOLO_CAP_MAX_PAGE((SOLO_CAP_EXT_SIZE(solo_dev) |
33 | - SOLO_CAP_PAGE_SIZE) >> 16) |
34 | | SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); |
35 | |
36 | /* XXX: Undocumented bits at b17 and b24 */ |
37 | if (solo_dev->type == SOLO_DEV_6110) { |
38 | /* NOTE: Ref driver has (62 << 24) here as well, but it causes |
39 | * wacked out frame timing on 4-port 6110. */ |
40 | solo_reg_write(solo_dev, SOLO_CAP_BTW, |
41 | data: (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | |
42 | SOLO_CAP_MAX_BANDWIDTH(36)); |
43 | } else { |
44 | solo_reg_write(solo_dev, SOLO_CAP_BTW, |
45 | data: (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | |
46 | SOLO_CAP_MAX_BANDWIDTH(32)); |
47 | } |
48 | |
49 | /* Set scale 1, 9 dimension */ |
50 | width = solo_dev->video_hsize; |
51 | height = solo_dev->video_vsize; |
52 | solo_reg_write(solo_dev, SOLO_DIM_SCALE1, |
53 | SOLO_DIM_H_MB_NUM(width / 16) | |
54 | SOLO_DIM_V_MB_NUM_FRAME(height / 8) | |
55 | SOLO_DIM_V_MB_NUM_FIELD(height / 16)); |
56 | |
57 | /* Set scale 2, 10 dimension */ |
58 | width = solo_dev->video_hsize / 2; |
59 | height = solo_dev->video_vsize; |
60 | solo_reg_write(solo_dev, SOLO_DIM_SCALE2, |
61 | SOLO_DIM_H_MB_NUM(width / 16) | |
62 | SOLO_DIM_V_MB_NUM_FRAME(height / 8) | |
63 | SOLO_DIM_V_MB_NUM_FIELD(height / 16)); |
64 | |
65 | /* Set scale 3, 11 dimension */ |
66 | width = solo_dev->video_hsize / 2; |
67 | height = solo_dev->video_vsize / 2; |
68 | solo_reg_write(solo_dev, SOLO_DIM_SCALE3, |
69 | SOLO_DIM_H_MB_NUM(width / 16) | |
70 | SOLO_DIM_V_MB_NUM_FRAME(height / 8) | |
71 | SOLO_DIM_V_MB_NUM_FIELD(height / 16)); |
72 | |
73 | /* Set scale 4, 12 dimension */ |
74 | width = solo_dev->video_hsize / 3; |
75 | height = solo_dev->video_vsize / 3; |
76 | solo_reg_write(solo_dev, SOLO_DIM_SCALE4, |
77 | SOLO_DIM_H_MB_NUM(width / 16) | |
78 | SOLO_DIM_V_MB_NUM_FRAME(height / 8) | |
79 | SOLO_DIM_V_MB_NUM_FIELD(height / 16)); |
80 | |
81 | /* Set scale 5, 13 dimension */ |
82 | width = solo_dev->video_hsize / 4; |
83 | height = solo_dev->video_vsize / 2; |
84 | solo_reg_write(solo_dev, SOLO_DIM_SCALE5, |
85 | SOLO_DIM_H_MB_NUM(width / 16) | |
86 | SOLO_DIM_V_MB_NUM_FRAME(height / 8) | |
87 | SOLO_DIM_V_MB_NUM_FIELD(height / 16)); |
88 | |
89 | /* Progressive */ |
90 | width = VI_PROG_HSIZE; |
91 | height = VI_PROG_VSIZE; |
92 | solo_reg_write(solo_dev, SOLO_DIM_PROG, |
93 | SOLO_DIM_H_MB_NUM(width / 16) | |
94 | SOLO_DIM_V_MB_NUM_FRAME(height / 16) | |
95 | SOLO_DIM_V_MB_NUM_FIELD(height / 16)); |
96 | |
97 | /* Clear OSD */ |
98 | solo_reg_write(solo_dev, SOLO_VE_OSD_CH, data: 0); |
99 | solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16); |
100 | solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, |
101 | data: 0xF0 << 16 | 0x80 << 8 | 0x80); |
102 | |
103 | if (solo_dev->type == SOLO_DEV_6010) |
104 | solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, |
105 | SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); |
106 | else |
107 | solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, SOLO_VE_OSD_V_DOUBLE |
108 | | SOLO_VE_OSD_H_SHADOW | SOLO_VE_OSD_V_SHADOW); |
109 | |
110 | /* Clear OSG buffer */ |
111 | buf = kzalloc(SOLO_EOSD_EXT_SIZE(solo_dev), GFP_KERNEL); |
112 | if (!buf) |
113 | return; |
114 | |
115 | for (i = 0; i < solo_dev->nr_chans; i++) { |
116 | solo_p2m_dma(solo_dev, wr: 1, sys_addr: buf, |
117 | SOLO_EOSD_EXT_ADDR + |
118 | (SOLO_EOSD_EXT_SIZE(solo_dev) * i), |
119 | SOLO_EOSD_EXT_SIZE(solo_dev), repeat: 0, ext_size: 0); |
120 | } |
121 | kfree(objp: buf); |
122 | } |
123 | |
124 | #define SOLO_OSD_WRITE_SIZE (16 * OSD_TEXT_MAX) |
125 | |
126 | /* Should be called with enable_lock held */ |
127 | int solo_osd_print(struct solo_enc_dev *solo_enc) |
128 | { |
129 | struct solo_dev *solo_dev = solo_enc->solo_dev; |
130 | u8 *str = solo_enc->osd_text; |
131 | u8 *buf = solo_enc->osd_buf; |
132 | u32 reg; |
133 | const struct font_desc *vga = find_font(name: "VGA8x16" ); |
134 | const u8 *vga_data; |
135 | int i, j; |
136 | |
137 | if (WARN_ON_ONCE(!vga)) |
138 | return -ENODEV; |
139 | |
140 | reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); |
141 | if (!*str) { |
142 | /* Disable OSD on this channel */ |
143 | reg &= ~(1 << solo_enc->ch); |
144 | goto out; |
145 | } |
146 | |
147 | memset(buf, 0, SOLO_OSD_WRITE_SIZE); |
148 | vga_data = (const u8 *)vga->data; |
149 | |
150 | for (i = 0; *str; i++, str++) { |
151 | for (j = 0; j < 16; j++) { |
152 | buf[(j << 1) | (i & 1) | ((i & ~1) << 4)] = |
153 | bitrev8(vga_data[(*str << 4) | j]); |
154 | } |
155 | } |
156 | |
157 | solo_p2m_dma(solo_dev, wr: 1, sys_addr: buf, |
158 | SOLO_EOSD_EXT_ADDR_CHAN(solo_dev, solo_enc->ch), |
159 | SOLO_OSD_WRITE_SIZE, repeat: 0, ext_size: 0); |
160 | |
161 | /* Enable OSD on this channel */ |
162 | reg |= (1 << solo_enc->ch); |
163 | |
164 | out: |
165 | solo_reg_write(solo_dev, SOLO_VE_OSD_CH, data: reg); |
166 | return 0; |
167 | } |
168 | |
169 | /* |
170 | * Set channel Quality Profile (0-3). |
171 | */ |
172 | void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, |
173 | unsigned int qp) |
174 | { |
175 | unsigned long flags; |
176 | unsigned int idx, reg; |
177 | |
178 | if ((ch > 31) || (qp > 3)) |
179 | return; |
180 | |
181 | if (solo_dev->type == SOLO_DEV_6010) |
182 | return; |
183 | |
184 | if (ch < 16) { |
185 | idx = 0; |
186 | reg = SOLO_VE_JPEG_QP_CH_L; |
187 | } else { |
188 | ch -= 16; |
189 | idx = 1; |
190 | reg = SOLO_VE_JPEG_QP_CH_H; |
191 | } |
192 | ch *= 2; |
193 | |
194 | spin_lock_irqsave(&solo_dev->jpeg_qp_lock, flags); |
195 | |
196 | solo_dev->jpeg_qp[idx] &= ~(3 << ch); |
197 | solo_dev->jpeg_qp[idx] |= (qp & 3) << ch; |
198 | |
199 | solo_reg_write(solo_dev, reg, data: solo_dev->jpeg_qp[idx]); |
200 | |
201 | spin_unlock_irqrestore(lock: &solo_dev->jpeg_qp_lock, flags); |
202 | } |
203 | |
204 | int solo_g_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch) |
205 | { |
206 | int idx; |
207 | |
208 | if (solo_dev->type == SOLO_DEV_6010) |
209 | return 2; |
210 | |
211 | if (WARN_ON_ONCE(ch > 31)) |
212 | return 2; |
213 | |
214 | if (ch < 16) { |
215 | idx = 0; |
216 | } else { |
217 | ch -= 16; |
218 | idx = 1; |
219 | } |
220 | ch *= 2; |
221 | |
222 | return (solo_dev->jpeg_qp[idx] >> ch) & 3; |
223 | } |
224 | |
225 | #define SOLO_QP_INIT 0xaaaaaaaa |
226 | |
227 | static void solo_jpeg_config(struct solo_dev *solo_dev) |
228 | { |
229 | if (solo_dev->type == SOLO_DEV_6010) { |
230 | solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, |
231 | data: (2 << 24) | (2 << 16) | (2 << 8) | 2); |
232 | } else { |
233 | solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, |
234 | data: (4 << 24) | (3 << 16) | (2 << 8) | 1); |
235 | } |
236 | |
237 | spin_lock_init(&solo_dev->jpeg_qp_lock); |
238 | |
239 | /* Initialize Quality Profile for all channels */ |
240 | solo_dev->jpeg_qp[0] = solo_dev->jpeg_qp[1] = SOLO_QP_INIT; |
241 | solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, SOLO_QP_INIT); |
242 | solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, SOLO_QP_INIT); |
243 | |
244 | solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG, |
245 | data: (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | |
246 | ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); |
247 | solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, data: 0xffffffff); |
248 | if (solo_dev->type == SOLO_DEV_6110) { |
249 | solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG1, |
250 | data: (0 << 16) | (30 << 8) | 60); |
251 | } |
252 | } |
253 | |
254 | static void solo_mp4e_config(struct solo_dev *solo_dev) |
255 | { |
256 | int i; |
257 | u32 cfg; |
258 | |
259 | solo_reg_write(solo_dev, SOLO_VE_CFG0, |
260 | SOLO_VE_INTR_CTRL(IRQ_LEVEL) | |
261 | SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) | |
262 | SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16)); |
263 | |
264 | |
265 | cfg = SOLO_VE_BYTE_ALIGN(2) | SOLO_VE_INSERT_INDEX |
266 | | SOLO_VE_MOTION_MODE(0); |
267 | if (solo_dev->type != SOLO_DEV_6010) { |
268 | cfg |= SOLO_VE_MPEG_SIZE_H( |
269 | (SOLO_MP4E_EXT_SIZE(solo_dev) >> 24) & 0x0f); |
270 | cfg |= SOLO_VE_JPEG_SIZE_H( |
271 | (SOLO_JPEG_EXT_SIZE(solo_dev) >> 24) & 0x0f); |
272 | } |
273 | solo_reg_write(solo_dev, SOLO_VE_CFG1, data: cfg); |
274 | |
275 | solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, data: 0); |
276 | solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, data: 0); |
277 | solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, data: 0); |
278 | if (solo_dev->type == SOLO_DEV_6110) |
279 | solo_reg_write(solo_dev, SOLO_VE_WMRK_ENABLE, data: 0); |
280 | solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, data: 0); |
281 | solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, data: 0); |
282 | |
283 | solo_reg_write(solo_dev, SOLO_VE_ATTR, |
284 | SOLO_VE_LITTLE_ENDIAN | |
285 | SOLO_COMP_ATTR_FCODE(1) | |
286 | SOLO_COMP_TIME_INC(0) | |
287 | SOLO_COMP_TIME_WIDTH(15) | |
288 | SOLO_DCT_INTERVAL(solo_dev->type == SOLO_DEV_6010 ? 9 : 10)); |
289 | |
290 | for (i = 0; i < solo_dev->nr_chans; i++) { |
291 | solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i), |
292 | data: (SOLO_EREF_EXT_ADDR(solo_dev) + |
293 | (i * SOLO_EREF_EXT_SIZE)) >> 16); |
294 | solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE_E(i), |
295 | data: (SOLO_EREF_EXT_ADDR(solo_dev) + |
296 | ((i + 16) * SOLO_EREF_EXT_SIZE)) >> 16); |
297 | } |
298 | |
299 | if (solo_dev->type == SOLO_DEV_6110) { |
300 | solo_reg_write(solo_dev, SOLO_VE_COMPT_MOT, data: 0x00040008); |
301 | } else { |
302 | for (i = 0; i < solo_dev->nr_chans; i++) |
303 | solo_reg_write(solo_dev, SOLO_VE_CH_MOT(i), data: 0x100); |
304 | } |
305 | } |
306 | |
307 | int solo_enc_init(struct solo_dev *solo_dev) |
308 | { |
309 | int i; |
310 | |
311 | solo_capture_config(solo_dev); |
312 | solo_mp4e_config(solo_dev); |
313 | solo_jpeg_config(solo_dev); |
314 | |
315 | for (i = 0; i < solo_dev->nr_chans; i++) { |
316 | solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), data: 0); |
317 | solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), data: 0); |
318 | } |
319 | |
320 | return 0; |
321 | } |
322 | |
323 | void solo_enc_exit(struct solo_dev *solo_dev) |
324 | { |
325 | int i; |
326 | |
327 | for (i = 0; i < solo_dev->nr_chans; i++) { |
328 | solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), data: 0); |
329 | solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), data: 0); |
330 | } |
331 | } |
332 | |