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/module.h> |
14 | #include <linux/videodev2.h> |
15 | #include <media/v4l2-ioctl.h> |
16 | |
17 | #include "solo6x10.h" |
18 | |
19 | #define SOLO_VCLK_DELAY 3 |
20 | #define SOLO_PROGRESSIVE_VSIZE 1024 |
21 | |
22 | #define SOLO_MOT_THRESH_W 64 |
23 | #define SOLO_MOT_THRESH_H 64 |
24 | #define SOLO_MOT_THRESH_SIZE 8192 |
25 | #define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) |
26 | #define SOLO_MOT_FLAG_SIZE 1024 |
27 | #define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16) |
28 | |
29 | static void solo_vin_config(struct solo_dev *solo_dev) |
30 | { |
31 | solo_dev->vin_hstart = 8; |
32 | solo_dev->vin_vstart = 2; |
33 | |
34 | solo_reg_write(solo_dev, SOLO_SYS_VCLK, |
35 | SOLO_VCLK_SELECT(2) | |
36 | SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) | |
37 | SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) | |
38 | SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) | |
39 | SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) | |
40 | SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) | |
41 | SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) | |
42 | SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) | |
43 | SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY)); |
44 | |
45 | solo_reg_write(solo_dev, SOLO_VI_ACT_I_P, |
46 | SOLO_VI_H_START(solo_dev->vin_hstart) | |
47 | SOLO_VI_V_START(solo_dev->vin_vstart) | |
48 | SOLO_VI_V_STOP(solo_dev->vin_vstart + |
49 | solo_dev->video_vsize)); |
50 | |
51 | solo_reg_write(solo_dev, SOLO_VI_ACT_I_S, |
52 | SOLO_VI_H_START(solo_dev->vout_hstart) | |
53 | SOLO_VI_V_START(solo_dev->vout_vstart) | |
54 | SOLO_VI_V_STOP(solo_dev->vout_vstart + |
55 | solo_dev->video_vsize)); |
56 | |
57 | solo_reg_write(solo_dev, SOLO_VI_ACT_P, |
58 | SOLO_VI_H_START(0) | |
59 | SOLO_VI_V_START(1) | |
60 | SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE)); |
61 | |
62 | solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, |
63 | SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); |
64 | |
65 | /* On 6110, initialize mozaic darkness strength */ |
66 | if (solo_dev->type == SOLO_DEV_6010) |
67 | solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, data: 0); |
68 | else |
69 | solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, data: 16 << 22); |
70 | |
71 | solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, data: 2); |
72 | |
73 | if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { |
74 | solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, |
75 | SOLO_VI_PB_USER_MODE); |
76 | solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, |
77 | SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246)); |
78 | solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, |
79 | SOLO_VI_PB_VSTART(4) | |
80 | SOLO_VI_PB_VSTOP(4 + 240)); |
81 | } else { |
82 | solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, |
83 | SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL); |
84 | solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, |
85 | SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294)); |
86 | solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, |
87 | SOLO_VI_PB_VSTART(4) | |
88 | SOLO_VI_PB_VSTOP(4 + 288)); |
89 | } |
90 | solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) | |
91 | SOLO_VI_PB_HSTOP(16 + 720)); |
92 | } |
93 | |
94 | static void solo_vout_config_cursor(struct solo_dev *dev) |
95 | { |
96 | int i; |
97 | |
98 | /* Load (blank) cursor bitmap mask (2bpp) */ |
99 | for (i = 0; i < 20; i++) |
100 | solo_reg_write(solo_dev: dev, SOLO_VO_CURSOR_MASK(i), data: 0); |
101 | |
102 | solo_reg_write(solo_dev: dev, SOLO_VO_CURSOR_POS, data: 0); |
103 | |
104 | solo_reg_write(solo_dev: dev, SOLO_VO_CURSOR_CLR, |
105 | data: (0x80 << 24) | (0x80 << 16) | (0x10 << 8) | 0x80); |
106 | solo_reg_write(solo_dev: dev, SOLO_VO_CURSOR_CLR2, data: (0xe0 << 8) | 0x80); |
107 | } |
108 | |
109 | static void solo_vout_config(struct solo_dev *solo_dev) |
110 | { |
111 | solo_dev->vout_hstart = 6; |
112 | solo_dev->vout_vstart = 8; |
113 | |
114 | solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, |
115 | data: solo_dev->video_type | |
116 | SOLO_VO_USER_COLOR_SET_NAV | |
117 | SOLO_VO_USER_COLOR_SET_NAH | |
118 | SOLO_VO_NA_COLOR_Y(0) | |
119 | SOLO_VO_NA_COLOR_CB(0) | |
120 | SOLO_VO_NA_COLOR_CR(0)); |
121 | |
122 | solo_reg_write(solo_dev, SOLO_VO_ACT_H, |
123 | SOLO_VO_H_START(solo_dev->vout_hstart) | |
124 | SOLO_VO_H_STOP(solo_dev->vout_hstart + |
125 | solo_dev->video_hsize)); |
126 | |
127 | solo_reg_write(solo_dev, SOLO_VO_ACT_V, |
128 | SOLO_VO_V_START(solo_dev->vout_vstart) | |
129 | SOLO_VO_V_STOP(solo_dev->vout_vstart + |
130 | solo_dev->video_vsize)); |
131 | |
132 | solo_reg_write(solo_dev, SOLO_VO_RANGE_HV, |
133 | SOLO_VO_H_LEN(solo_dev->video_hsize) | |
134 | SOLO_VO_V_LEN(solo_dev->video_vsize)); |
135 | |
136 | /* Border & background colors */ |
137 | solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, |
138 | data: (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); |
139 | solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, |
140 | data: (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); |
141 | solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, |
142 | data: (16 << 24) | (128 << 16) | (16 << 8) | 128); |
143 | |
144 | solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); |
145 | |
146 | solo_reg_write(solo_dev, SOLO_VI_WIN_SW, data: 0); |
147 | |
148 | solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, data: 0); |
149 | solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, data: 0); |
150 | |
151 | solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | |
152 | SOLO_VO_DISP_ERASE_COUNT(8) | |
153 | SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); |
154 | |
155 | |
156 | solo_vout_config_cursor(dev: solo_dev); |
157 | |
158 | /* Enable channels we support */ |
159 | solo_reg_write(solo_dev, SOLO_VI_CH_ENA, |
160 | data: (1 << solo_dev->nr_chans) - 1); |
161 | } |
162 | |
163 | static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, |
164 | u16 val, int reg_size) |
165 | { |
166 | __le16 *buf; |
167 | const int n = 64, size = n * sizeof(*buf); |
168 | int i, ret = 0; |
169 | |
170 | buf = kmalloc(size, GFP_KERNEL); |
171 | if (!buf) |
172 | return -ENOMEM; |
173 | |
174 | for (i = 0; i < n; i++) |
175 | buf[i] = cpu_to_le16(val); |
176 | |
177 | for (i = 0; i < reg_size; i += size) { |
178 | ret = solo_p2m_dma(solo_dev, wr: 1, sys_addr: buf, |
179 | SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, |
180 | size, repeat: 0, ext_size: 0); |
181 | |
182 | if (ret) |
183 | break; |
184 | } |
185 | |
186 | kfree(objp: buf); |
187 | return ret; |
188 | } |
189 | |
190 | int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) |
191 | { |
192 | if (ch > solo_dev->nr_chans) |
193 | return -EINVAL; |
194 | |
195 | return solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + |
196 | (ch * SOLO_MOT_THRESH_SIZE * 2), |
197 | val, SOLO_MOT_THRESH_SIZE); |
198 | } |
199 | |
200 | int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, |
201 | const u16 *thresholds) |
202 | { |
203 | const unsigned size = sizeof(u16) * 64; |
204 | u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; |
205 | __le16 *buf; |
206 | int x, y; |
207 | int ret = 0; |
208 | |
209 | buf = kzalloc(size, GFP_KERNEL); |
210 | if (buf == NULL) |
211 | return -ENOMEM; |
212 | for (y = 0; y < SOLO_MOTION_SZ; y++) { |
213 | for (x = 0; x < SOLO_MOTION_SZ; x++) |
214 | buf[x] = cpu_to_le16(thresholds[y * SOLO_MOTION_SZ + x]); |
215 | ret |= solo_p2m_dma(solo_dev, wr: 1, sys_addr: buf, |
216 | SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * size, |
217 | size, repeat: 0, ext_size: 0); |
218 | } |
219 | kfree(objp: buf); |
220 | return ret; |
221 | } |
222 | |
223 | /* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k |
224 | * threshold and working table for each channel. At least that's what the |
225 | * spec says. However, this code (taken from rdk) has some mystery 8k |
226 | * block right after the flag area, before the first thresh table. */ |
227 | static void solo_motion_config(struct solo_dev *solo_dev) |
228 | { |
229 | int i; |
230 | |
231 | for (i = 0; i < solo_dev->nr_chans; i++) { |
232 | /* Clear motion flag area */ |
233 | solo_dma_vin_region(solo_dev, off: i * SOLO_MOT_FLAG_SIZE, val: 0x0000, |
234 | SOLO_MOT_FLAG_SIZE); |
235 | |
236 | /* Clear working cache table */ |
237 | solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + |
238 | (i * SOLO_MOT_THRESH_SIZE * 2) + |
239 | SOLO_MOT_THRESH_SIZE, val: 0x0000, |
240 | SOLO_MOT_THRESH_SIZE); |
241 | |
242 | /* Set default threshold table */ |
243 | solo_set_motion_threshold(solo_dev, ch: i, SOLO_DEF_MOT_THRESH); |
244 | } |
245 | |
246 | /* Default motion settings */ |
247 | solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) | |
248 | (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); |
249 | solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, |
250 | SOLO_VI_MOTION_FRAME_COUNT(3) | |
251 | SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) |
252 | /* | SOLO_VI_MOTION_INTR_START_STOP */ |
253 | | SOLO_VI_MOTION_SAMPLE_COUNT(10)); |
254 | |
255 | solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, data: 0); |
256 | solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, data: 0); |
257 | } |
258 | |
259 | int solo_disp_init(struct solo_dev *solo_dev) |
260 | { |
261 | int i; |
262 | |
263 | solo_dev->video_hsize = 704; |
264 | if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { |
265 | solo_dev->video_vsize = 240; |
266 | solo_dev->fps = 30; |
267 | } else { |
268 | solo_dev->video_vsize = 288; |
269 | solo_dev->fps = 25; |
270 | } |
271 | |
272 | solo_vin_config(solo_dev); |
273 | solo_motion_config(solo_dev); |
274 | solo_vout_config(solo_dev); |
275 | |
276 | for (i = 0; i < solo_dev->nr_chans; i++) |
277 | solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), data: 1); |
278 | |
279 | return 0; |
280 | } |
281 | |
282 | void solo_disp_exit(struct solo_dev *solo_dev) |
283 | { |
284 | int i; |
285 | |
286 | solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, data: 0); |
287 | solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, data: 0); |
288 | solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, data: 0); |
289 | |
290 | for (i = 0; i < solo_dev->nr_chans; i++) { |
291 | solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), data: 0); |
292 | solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), data: 0); |
293 | solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), data: 0); |
294 | } |
295 | |
296 | /* Set default border */ |
297 | for (i = 0; i < 5; i++) |
298 | solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), data: 0); |
299 | |
300 | for (i = 0; i < 5; i++) |
301 | solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), data: 0); |
302 | |
303 | solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, data: 0); |
304 | solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, data: 0); |
305 | |
306 | solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), data: 0); |
307 | solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), data: 0); |
308 | solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), data: 0); |
309 | |
310 | solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), data: 0); |
311 | solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), data: 0); |
312 | solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), data: 0); |
313 | } |
314 | |