xref: /linux/drivers/media/pci/hws/hws_v4l2_ioctl.c (revision 3d5e48944e824bddc20d7b874e784f7b279636fe)
1*ba07fd2fSBen Hoff // SPDX-License-Identifier: GPL-2.0-only
2*ba07fd2fSBen Hoff #include <linux/kernel.h>
3*ba07fd2fSBen Hoff #include <linux/string.h>
4*ba07fd2fSBen Hoff #include <linux/pci.h>
5*ba07fd2fSBen Hoff #include <linux/errno.h>
6*ba07fd2fSBen Hoff #include <linux/io.h>
7*ba07fd2fSBen Hoff #include <linux/math64.h>
8*ba07fd2fSBen Hoff 
9*ba07fd2fSBen Hoff #include <media/v4l2-ioctl.h>
10*ba07fd2fSBen Hoff #include <media/v4l2-dev.h>
11*ba07fd2fSBen Hoff #include <media/v4l2-dv-timings.h>
12*ba07fd2fSBen Hoff #include <media/videobuf2-core.h>
13*ba07fd2fSBen Hoff #include <media/videobuf2-v4l2.h>
14*ba07fd2fSBen Hoff 
15*ba07fd2fSBen Hoff #include "hws.h"
16*ba07fd2fSBen Hoff #include "hws_reg.h"
17*ba07fd2fSBen Hoff #include "hws_video.h"
18*ba07fd2fSBen Hoff #include "hws_v4l2_ioctl.h"
19*ba07fd2fSBen Hoff 
20*ba07fd2fSBen Hoff struct hws_dv_mode {
21*ba07fd2fSBen Hoff 	struct v4l2_dv_timings timings;
22*ba07fd2fSBen Hoff 	u32 refresh_hz;
23*ba07fd2fSBen Hoff };
24*ba07fd2fSBen Hoff 
25*ba07fd2fSBen Hoff static const struct hws_dv_mode *
26*ba07fd2fSBen Hoff hws_find_dv_by_wh(u32 w, u32 h, bool interlaced);
27*ba07fd2fSBen Hoff static const struct hws_dv_mode *
28*ba07fd2fSBen Hoff hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps);
29*ba07fd2fSBen Hoff static u32 hws_get_live_fps(struct hws_video *vid);
30*ba07fd2fSBen Hoff static u32 hws_input_status(struct hws_video *vid);
31*ba07fd2fSBen Hoff static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps,
32*ba07fd2fSBen Hoff 			       struct v4l2_dv_timings *timings);
33*ba07fd2fSBen Hoff 
34*ba07fd2fSBen Hoff static const struct hws_dv_mode hws_dv_modes[] = {
35*ba07fd2fSBen Hoff 	{
36*ba07fd2fSBen Hoff 		{
37*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
38*ba07fd2fSBen Hoff 			.bt = {
39*ba07fd2fSBen Hoff 				.width = 1920,
40*ba07fd2fSBen Hoff 				.height = 1080,
41*ba07fd2fSBen Hoff 				.hfrontporch = 88,
42*ba07fd2fSBen Hoff 				.hsync = 44,
43*ba07fd2fSBen Hoff 				.hbackporch = 148,
44*ba07fd2fSBen Hoff 				.vfrontporch = 4,
45*ba07fd2fSBen Hoff 				.vsync = 5,
46*ba07fd2fSBen Hoff 				.vbackporch = 36,
47*ba07fd2fSBen Hoff 				.pixelclock = 148500000,
48*ba07fd2fSBen Hoff 				.polarities = V4L2_DV_VSYNC_POS_POL |
49*ba07fd2fSBen Hoff 					      V4L2_DV_HSYNC_POS_POL,
50*ba07fd2fSBen Hoff 				.interlaced = 0,
51*ba07fd2fSBen Hoff 			},
52*ba07fd2fSBen Hoff 		},
53*ba07fd2fSBen Hoff 		60,
54*ba07fd2fSBen Hoff 	},
55*ba07fd2fSBen Hoff 	{
56*ba07fd2fSBen Hoff 		{
57*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
58*ba07fd2fSBen Hoff 			.bt = {
59*ba07fd2fSBen Hoff 				.width = 1920,
60*ba07fd2fSBen Hoff 				.height = 1080,
61*ba07fd2fSBen Hoff 				.hfrontporch = 88,
62*ba07fd2fSBen Hoff 				.hsync = 44,
63*ba07fd2fSBen Hoff 				.hbackporch = 148,
64*ba07fd2fSBen Hoff 				.vfrontporch = 4,
65*ba07fd2fSBen Hoff 				.vsync = 5,
66*ba07fd2fSBen Hoff 				.vbackporch = 36,
67*ba07fd2fSBen Hoff 				.pixelclock = 74250000,
68*ba07fd2fSBen Hoff 				.polarities = V4L2_DV_VSYNC_POS_POL |
69*ba07fd2fSBen Hoff 					      V4L2_DV_HSYNC_POS_POL,
70*ba07fd2fSBen Hoff 				.interlaced = 0,
71*ba07fd2fSBen Hoff 			},
72*ba07fd2fSBen Hoff 		},
73*ba07fd2fSBen Hoff 		30,
74*ba07fd2fSBen Hoff 	},
75*ba07fd2fSBen Hoff 	{
76*ba07fd2fSBen Hoff 		{
77*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
78*ba07fd2fSBen Hoff 			.bt = {
79*ba07fd2fSBen Hoff 				.width = 1280,
80*ba07fd2fSBen Hoff 				.height = 720,
81*ba07fd2fSBen Hoff 				.hfrontporch = 110,
82*ba07fd2fSBen Hoff 				.hsync = 40,
83*ba07fd2fSBen Hoff 				.hbackporch = 220,
84*ba07fd2fSBen Hoff 				.vfrontporch = 5,
85*ba07fd2fSBen Hoff 				.vsync = 5,
86*ba07fd2fSBen Hoff 				.vbackporch = 20,
87*ba07fd2fSBen Hoff 				.pixelclock = 74250000,
88*ba07fd2fSBen Hoff 				.polarities = V4L2_DV_VSYNC_POS_POL |
89*ba07fd2fSBen Hoff 					      V4L2_DV_HSYNC_POS_POL,
90*ba07fd2fSBen Hoff 				.interlaced = 0,
91*ba07fd2fSBen Hoff 			},
92*ba07fd2fSBen Hoff 		},
93*ba07fd2fSBen Hoff 		60,
94*ba07fd2fSBen Hoff 	},
95*ba07fd2fSBen Hoff 	{
96*ba07fd2fSBen Hoff 		{
97*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
98*ba07fd2fSBen Hoff 			.bt = {
99*ba07fd2fSBen Hoff 				.width = 720,
100*ba07fd2fSBen Hoff 				.height = 480,
101*ba07fd2fSBen Hoff 				.interlaced = 0,
102*ba07fd2fSBen Hoff 			},
103*ba07fd2fSBen Hoff 		},
104*ba07fd2fSBen Hoff 		60,
105*ba07fd2fSBen Hoff 	},
106*ba07fd2fSBen Hoff 	{
107*ba07fd2fSBen Hoff 		{
108*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
109*ba07fd2fSBen Hoff 			.bt = {
110*ba07fd2fSBen Hoff 				.width = 720,
111*ba07fd2fSBen Hoff 				.height = 576,
112*ba07fd2fSBen Hoff 				.interlaced = 0,
113*ba07fd2fSBen Hoff 			},
114*ba07fd2fSBen Hoff 		},
115*ba07fd2fSBen Hoff 		50,
116*ba07fd2fSBen Hoff 	},
117*ba07fd2fSBen Hoff 	{
118*ba07fd2fSBen Hoff 		{
119*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
120*ba07fd2fSBen Hoff 			.bt = {
121*ba07fd2fSBen Hoff 				.width = 800,
122*ba07fd2fSBen Hoff 				.height = 600,
123*ba07fd2fSBen Hoff 				.interlaced = 0,
124*ba07fd2fSBen Hoff 			},
125*ba07fd2fSBen Hoff 		},
126*ba07fd2fSBen Hoff 		60,
127*ba07fd2fSBen Hoff 	},
128*ba07fd2fSBen Hoff 	{
129*ba07fd2fSBen Hoff 		{
130*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
131*ba07fd2fSBen Hoff 			.bt = {
132*ba07fd2fSBen Hoff 				.width = 640,
133*ba07fd2fSBen Hoff 				.height = 480,
134*ba07fd2fSBen Hoff 				.interlaced = 0,
135*ba07fd2fSBen Hoff 			},
136*ba07fd2fSBen Hoff 		},
137*ba07fd2fSBen Hoff 		60,
138*ba07fd2fSBen Hoff 	},
139*ba07fd2fSBen Hoff 	{
140*ba07fd2fSBen Hoff 		{
141*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
142*ba07fd2fSBen Hoff 			.bt = {
143*ba07fd2fSBen Hoff 				.width = 1024,
144*ba07fd2fSBen Hoff 				.height = 768,
145*ba07fd2fSBen Hoff 				.interlaced = 0,
146*ba07fd2fSBen Hoff 			},
147*ba07fd2fSBen Hoff 		},
148*ba07fd2fSBen Hoff 		60,
149*ba07fd2fSBen Hoff 	},
150*ba07fd2fSBen Hoff 	{
151*ba07fd2fSBen Hoff 		{
152*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
153*ba07fd2fSBen Hoff 			.bt = {
154*ba07fd2fSBen Hoff 				.width = 1280,
155*ba07fd2fSBen Hoff 				.height = 768,
156*ba07fd2fSBen Hoff 				.interlaced = 0,
157*ba07fd2fSBen Hoff 			},
158*ba07fd2fSBen Hoff 		},
159*ba07fd2fSBen Hoff 		60,
160*ba07fd2fSBen Hoff 	},
161*ba07fd2fSBen Hoff 	{
162*ba07fd2fSBen Hoff 		{
163*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
164*ba07fd2fSBen Hoff 			.bt = {
165*ba07fd2fSBen Hoff 				.width = 1280,
166*ba07fd2fSBen Hoff 				.height = 800,
167*ba07fd2fSBen Hoff 				.interlaced = 0,
168*ba07fd2fSBen Hoff 			},
169*ba07fd2fSBen Hoff 		},
170*ba07fd2fSBen Hoff 		60,
171*ba07fd2fSBen Hoff 	},
172*ba07fd2fSBen Hoff 	{
173*ba07fd2fSBen Hoff 		{
174*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
175*ba07fd2fSBen Hoff 			.bt = {
176*ba07fd2fSBen Hoff 				.width = 1280,
177*ba07fd2fSBen Hoff 				.height = 1024,
178*ba07fd2fSBen Hoff 				.interlaced = 0,
179*ba07fd2fSBen Hoff 			},
180*ba07fd2fSBen Hoff 		},
181*ba07fd2fSBen Hoff 		60,
182*ba07fd2fSBen Hoff 	},
183*ba07fd2fSBen Hoff 	{
184*ba07fd2fSBen Hoff 		{
185*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
186*ba07fd2fSBen Hoff 			.bt = {
187*ba07fd2fSBen Hoff 				.width = 1360,
188*ba07fd2fSBen Hoff 				.height = 768,
189*ba07fd2fSBen Hoff 				.interlaced = 0,
190*ba07fd2fSBen Hoff 			},
191*ba07fd2fSBen Hoff 		},
192*ba07fd2fSBen Hoff 		60,
193*ba07fd2fSBen Hoff 	},
194*ba07fd2fSBen Hoff 	{
195*ba07fd2fSBen Hoff 		{
196*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
197*ba07fd2fSBen Hoff 			.bt = {
198*ba07fd2fSBen Hoff 				.width = 1440,
199*ba07fd2fSBen Hoff 				.height = 900,
200*ba07fd2fSBen Hoff 				.interlaced = 0,
201*ba07fd2fSBen Hoff 			},
202*ba07fd2fSBen Hoff 		},
203*ba07fd2fSBen Hoff 		60,
204*ba07fd2fSBen Hoff 	},
205*ba07fd2fSBen Hoff 	{
206*ba07fd2fSBen Hoff 		{
207*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
208*ba07fd2fSBen Hoff 			.bt = {
209*ba07fd2fSBen Hoff 				.width = 1680,
210*ba07fd2fSBen Hoff 				.height = 1050,
211*ba07fd2fSBen Hoff 				.interlaced = 0,
212*ba07fd2fSBen Hoff 			},
213*ba07fd2fSBen Hoff 		},
214*ba07fd2fSBen Hoff 		60,
215*ba07fd2fSBen Hoff 	},
216*ba07fd2fSBen Hoff 	/* Portrait */
217*ba07fd2fSBen Hoff 	{
218*ba07fd2fSBen Hoff 		{
219*ba07fd2fSBen Hoff 			.type = V4L2_DV_BT_656_1120,
220*ba07fd2fSBen Hoff 			.bt = {
221*ba07fd2fSBen Hoff 				.width = 1080,
222*ba07fd2fSBen Hoff 				.height = 1920,
223*ba07fd2fSBen Hoff 				.interlaced = 0,
224*ba07fd2fSBen Hoff 			},
225*ba07fd2fSBen Hoff 		},
226*ba07fd2fSBen Hoff 		60,
227*ba07fd2fSBen Hoff 	},
228*ba07fd2fSBen Hoff };
229*ba07fd2fSBen Hoff 
230*ba07fd2fSBen Hoff static const size_t hws_dv_modes_cnt = ARRAY_SIZE(hws_dv_modes);
231*ba07fd2fSBen Hoff 
232*ba07fd2fSBen Hoff /* YUYV: 16 bpp; align to 64 as you did elsewhere */
233*ba07fd2fSBen Hoff static inline u32 hws_calc_bpl_yuyv(u32 w)     { return ALIGN(w * 2, 64); }
234*ba07fd2fSBen Hoff static inline u32 hws_calc_size_yuyv(u32 w, u32 h) { return hws_calc_bpl_yuyv(w) * h; }
235*ba07fd2fSBen Hoff static inline u32 hws_calc_half_size(u32 sizeimage)
236*ba07fd2fSBen Hoff {
237*ba07fd2fSBen Hoff 	return sizeimage / 2;
238*ba07fd2fSBen Hoff }
239*ba07fd2fSBen Hoff 
240*ba07fd2fSBen Hoff static inline void hws_hw_write_bchs(struct hws_pcie_dev *hws, unsigned int ch,
241*ba07fd2fSBen Hoff 				     u8 br, u8 co, u8 hu, u8 sa)
242*ba07fd2fSBen Hoff {
243*ba07fd2fSBen Hoff 	u32 packed = (sa << 24) | (hu << 16) | (co << 8) | br;
244*ba07fd2fSBen Hoff 
245*ba07fd2fSBen Hoff 	if (!hws || !hws->bar0_base || ch >= hws->max_channels)
246*ba07fd2fSBen Hoff 		return;
247*ba07fd2fSBen Hoff 	writel_relaxed(packed, hws->bar0_base + HWS_REG_BCHS(ch));
248*ba07fd2fSBen Hoff 	(void)readl(hws->bar0_base + HWS_REG_BCHS(ch)); /* post write */
249*ba07fd2fSBen Hoff }
250*ba07fd2fSBen Hoff 
251*ba07fd2fSBen Hoff /* Helper: find a supported DV mode by W/H + interlace flag */
252*ba07fd2fSBen Hoff static const struct hws_dv_mode *
253*ba07fd2fSBen Hoff hws_match_supported_dv(const struct v4l2_dv_timings *req)
254*ba07fd2fSBen Hoff {
255*ba07fd2fSBen Hoff 	const struct v4l2_bt_timings *bt;
256*ba07fd2fSBen Hoff 	u32 fps;
257*ba07fd2fSBen Hoff 
258*ba07fd2fSBen Hoff 	if (!req || req->type != V4L2_DV_BT_656_1120)
259*ba07fd2fSBen Hoff 		return NULL;
260*ba07fd2fSBen Hoff 
261*ba07fd2fSBen Hoff 	bt = &req->bt;
262*ba07fd2fSBen Hoff 	fps = 0;
263*ba07fd2fSBen Hoff 	if (bt->pixelclock) {
264*ba07fd2fSBen Hoff 		u32 total_w = bt->width + bt->hfrontporch + bt->hsync +
265*ba07fd2fSBen Hoff 			      bt->hbackporch;
266*ba07fd2fSBen Hoff 		u32 total_h = bt->height + bt->vfrontporch + bt->vsync +
267*ba07fd2fSBen Hoff 			      bt->vbackporch;
268*ba07fd2fSBen Hoff 
269*ba07fd2fSBen Hoff 		if (total_w && total_h)
270*ba07fd2fSBen Hoff 			fps = DIV_ROUND_CLOSEST_ULL((u64)bt->pixelclock,
271*ba07fd2fSBen Hoff 						    (u64)total_w * total_h);
272*ba07fd2fSBen Hoff 	}
273*ba07fd2fSBen Hoff 	if (fps) {
274*ba07fd2fSBen Hoff 		const struct hws_dv_mode *exact =
275*ba07fd2fSBen Hoff 			hws_find_dv_by_wh_fps(bt->width, bt->height,
276*ba07fd2fSBen Hoff 					      !!bt->interlaced, fps);
277*ba07fd2fSBen Hoff 		if (exact)
278*ba07fd2fSBen Hoff 			return exact;
279*ba07fd2fSBen Hoff 	}
280*ba07fd2fSBen Hoff 	return hws_find_dv_by_wh(bt->width, bt->height, !!bt->interlaced);
281*ba07fd2fSBen Hoff }
282*ba07fd2fSBen Hoff 
283*ba07fd2fSBen Hoff /* Helper: find a supported DV mode by W/H + interlace flag */
284*ba07fd2fSBen Hoff static const struct hws_dv_mode *
285*ba07fd2fSBen Hoff hws_find_dv_by_wh(u32 w, u32 h, bool interlaced)
286*ba07fd2fSBen Hoff {
287*ba07fd2fSBen Hoff 	size_t i;
288*ba07fd2fSBen Hoff 
289*ba07fd2fSBen Hoff 	for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) {
290*ba07fd2fSBen Hoff 		const struct hws_dv_mode *t = &hws_dv_modes[i];
291*ba07fd2fSBen Hoff 		const struct v4l2_bt_timings *bt = &t->timings.bt;
292*ba07fd2fSBen Hoff 
293*ba07fd2fSBen Hoff 		if (t->timings.type != V4L2_DV_BT_656_1120)
294*ba07fd2fSBen Hoff 			continue;
295*ba07fd2fSBen Hoff 
296*ba07fd2fSBen Hoff 		if (bt->width == w && bt->height == h &&
297*ba07fd2fSBen Hoff 		    !!bt->interlaced == interlaced)
298*ba07fd2fSBen Hoff 			return t;
299*ba07fd2fSBen Hoff 	}
300*ba07fd2fSBen Hoff 	return NULL;
301*ba07fd2fSBen Hoff }
302*ba07fd2fSBen Hoff 
303*ba07fd2fSBen Hoff static const struct hws_dv_mode *
304*ba07fd2fSBen Hoff hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps)
305*ba07fd2fSBen Hoff {
306*ba07fd2fSBen Hoff 	size_t i;
307*ba07fd2fSBen Hoff 
308*ba07fd2fSBen Hoff 	for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) {
309*ba07fd2fSBen Hoff 		const struct hws_dv_mode *t = &hws_dv_modes[i];
310*ba07fd2fSBen Hoff 		const struct v4l2_bt_timings *bt = &t->timings.bt;
311*ba07fd2fSBen Hoff 
312*ba07fd2fSBen Hoff 		if (t->timings.type != V4L2_DV_BT_656_1120)
313*ba07fd2fSBen Hoff 			continue;
314*ba07fd2fSBen Hoff 
315*ba07fd2fSBen Hoff 		if (bt->width == w && bt->height == h &&
316*ba07fd2fSBen Hoff 		    !!bt->interlaced == interlaced &&
317*ba07fd2fSBen Hoff 		    t->refresh_hz == fps)
318*ba07fd2fSBen Hoff 			return t;
319*ba07fd2fSBen Hoff 	}
320*ba07fd2fSBen Hoff 	return NULL;
321*ba07fd2fSBen Hoff }
322*ba07fd2fSBen Hoff 
323*ba07fd2fSBen Hoff static bool hws_get_live_dv_geometry(struct hws_video *vid,
324*ba07fd2fSBen Hoff 				     u32 *w, u32 *h, bool *interlaced)
325*ba07fd2fSBen Hoff {
326*ba07fd2fSBen Hoff 	struct hws_pcie_dev *pdx;
327*ba07fd2fSBen Hoff 	u32 reg;
328*ba07fd2fSBen Hoff 
329*ba07fd2fSBen Hoff 	if (!vid)
330*ba07fd2fSBen Hoff 		return false;
331*ba07fd2fSBen Hoff 
332*ba07fd2fSBen Hoff 	pdx = vid->parent;
333*ba07fd2fSBen Hoff 	if (!pdx || !pdx->bar0_base)
334*ba07fd2fSBen Hoff 		return false;
335*ba07fd2fSBen Hoff 
336*ba07fd2fSBen Hoff 	reg = readl(pdx->bar0_base + HWS_REG_IN_RES(vid->channel_index));
337*ba07fd2fSBen Hoff 	if (!reg || reg == 0xFFFFFFFF)
338*ba07fd2fSBen Hoff 		return false;
339*ba07fd2fSBen Hoff 
340*ba07fd2fSBen Hoff 	if (w)
341*ba07fd2fSBen Hoff 		*w = reg & 0xFFFF;
342*ba07fd2fSBen Hoff 	if (h)
343*ba07fd2fSBen Hoff 		*h = (reg >> 16) & 0xFFFF;
344*ba07fd2fSBen Hoff 	if (interlaced) {
345*ba07fd2fSBen Hoff 		reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS);
346*ba07fd2fSBen Hoff 		*interlaced = !!(reg & BIT(8 + vid->channel_index));
347*ba07fd2fSBen Hoff 	}
348*ba07fd2fSBen Hoff 	return true;
349*ba07fd2fSBen Hoff }
350*ba07fd2fSBen Hoff 
351*ba07fd2fSBen Hoff static u32 hws_get_live_fps(struct hws_video *vid)
352*ba07fd2fSBen Hoff {
353*ba07fd2fSBen Hoff 	struct hws_pcie_dev *pdx;
354*ba07fd2fSBen Hoff 	u32 fps;
355*ba07fd2fSBen Hoff 
356*ba07fd2fSBen Hoff 	if (!vid)
357*ba07fd2fSBen Hoff 		return 0;
358*ba07fd2fSBen Hoff 
359*ba07fd2fSBen Hoff 	pdx = vid->parent;
360*ba07fd2fSBen Hoff 	if (!pdx || !pdx->bar0_base)
361*ba07fd2fSBen Hoff 		return 0;
362*ba07fd2fSBen Hoff 
363*ba07fd2fSBen Hoff 	fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(vid->channel_index));
364*ba07fd2fSBen Hoff 	if (!fps || fps == 0xFFFFFFFF || fps > 240)
365*ba07fd2fSBen Hoff 		return 0;
366*ba07fd2fSBen Hoff 
367*ba07fd2fSBen Hoff 	return fps;
368*ba07fd2fSBen Hoff }
369*ba07fd2fSBen Hoff 
370*ba07fd2fSBen Hoff static u32 hws_pick_fps_from_mode(u32 w, u32 h, bool interlaced)
371*ba07fd2fSBen Hoff {
372*ba07fd2fSBen Hoff 	const struct hws_dv_mode *m = hws_find_dv_by_wh(w, h, interlaced);
373*ba07fd2fSBen Hoff 
374*ba07fd2fSBen Hoff 	if (m && m->refresh_hz)
375*ba07fd2fSBen Hoff 		return m->refresh_hz;
376*ba07fd2fSBen Hoff 	/* Fallback to a sane default */
377*ba07fd2fSBen Hoff 	return 60;
378*ba07fd2fSBen Hoff }
379*ba07fd2fSBen Hoff 
380*ba07fd2fSBen Hoff static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps,
381*ba07fd2fSBen Hoff 			       struct v4l2_dv_timings *timings)
382*ba07fd2fSBen Hoff {
383*ba07fd2fSBen Hoff 	const struct hws_dv_mode *m;
384*ba07fd2fSBen Hoff 
385*ba07fd2fSBen Hoff 	m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL;
386*ba07fd2fSBen Hoff 	if (!m)
387*ba07fd2fSBen Hoff 		m = hws_find_dv_by_wh(w, h, interlace);
388*ba07fd2fSBen Hoff 	if (!m)
389*ba07fd2fSBen Hoff 		return -ENOLINK;
390*ba07fd2fSBen Hoff 
391*ba07fd2fSBen Hoff 	*timings = m->timings;
392*ba07fd2fSBen Hoff 	return 0;
393*ba07fd2fSBen Hoff }
394*ba07fd2fSBen Hoff 
395*ba07fd2fSBen Hoff static u32 hws_input_status(struct hws_video *vid)
396*ba07fd2fSBen Hoff {
397*ba07fd2fSBen Hoff 	struct hws_pcie_dev *pdx;
398*ba07fd2fSBen Hoff 	u32 reg;
399*ba07fd2fSBen Hoff 
400*ba07fd2fSBen Hoff 	if (!vid)
401*ba07fd2fSBen Hoff 		return V4L2_IN_ST_NO_SIGNAL;
402*ba07fd2fSBen Hoff 
403*ba07fd2fSBen Hoff 	pdx = vid->parent;
404*ba07fd2fSBen Hoff 	if (!pdx || !pdx->bar0_base)
405*ba07fd2fSBen Hoff 		return V4L2_IN_ST_NO_SIGNAL;
406*ba07fd2fSBen Hoff 
407*ba07fd2fSBen Hoff 	reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS);
408*ba07fd2fSBen Hoff 	if (reg == 0xffffffff)
409*ba07fd2fSBen Hoff 		return V4L2_IN_ST_NO_SIGNAL;
410*ba07fd2fSBen Hoff 
411*ba07fd2fSBen Hoff 	return (reg & BIT(vid->channel_index)) ? 0 : V4L2_IN_ST_NO_SIGNAL;
412*ba07fd2fSBen Hoff }
413*ba07fd2fSBen Hoff 
414*ba07fd2fSBen Hoff /* Query the *current detected* DV timings on the input.
415*ba07fd2fSBen Hoff  * If you have a real hardware detector, call it here; otherwise we
416*ba07fd2fSBen Hoff  * derive from the cached pix state and map to the closest supported DV mode.
417*ba07fd2fSBen Hoff  */
418*ba07fd2fSBen Hoff int hws_vidioc_query_dv_timings(struct file *file, void *fh,
419*ba07fd2fSBen Hoff 				struct v4l2_dv_timings *timings)
420*ba07fd2fSBen Hoff {
421*ba07fd2fSBen Hoff 	struct hws_video *vid = video_drvdata(file);
422*ba07fd2fSBen Hoff 	u32 w, h;
423*ba07fd2fSBen Hoff 	u32 fps;
424*ba07fd2fSBen Hoff 	bool interlace;
425*ba07fd2fSBen Hoff 
426*ba07fd2fSBen Hoff 	if (!timings)
427*ba07fd2fSBen Hoff 		return -EINVAL;
428*ba07fd2fSBen Hoff 
429*ba07fd2fSBen Hoff 	w = vid->pix.width;
430*ba07fd2fSBen Hoff 	h = vid->pix.height;
431*ba07fd2fSBen Hoff 	interlace = vid->pix.interlaced;
432*ba07fd2fSBen Hoff 	hws_get_live_dv_geometry(vid, &w, &h, &interlace);
433*ba07fd2fSBen Hoff 	fps = hws_get_live_fps(vid);
434*ba07fd2fSBen Hoff 	if (!fps)
435*ba07fd2fSBen Hoff 		fps = vid->current_fps ? vid->current_fps :
436*ba07fd2fSBen Hoff 		      hws_pick_fps_from_mode(w, h, interlace);
437*ba07fd2fSBen Hoff 
438*ba07fd2fSBen Hoff 	return hws_fill_dv_timings(w, h, interlace, fps, timings);
439*ba07fd2fSBen Hoff }
440*ba07fd2fSBen Hoff 
441*ba07fd2fSBen Hoff /* Enumerate the Nth supported DV timings from our static table. */
442*ba07fd2fSBen Hoff int hws_vidioc_enum_dv_timings(struct file *file, void *fh,
443*ba07fd2fSBen Hoff 			       struct v4l2_enum_dv_timings *edv)
444*ba07fd2fSBen Hoff {
445*ba07fd2fSBen Hoff 	struct hws_video *vid = video_drvdata(file);
446*ba07fd2fSBen Hoff 	const struct hws_dv_mode *m;
447*ba07fd2fSBen Hoff 	u32 w, h;
448*ba07fd2fSBen Hoff 	u32 fps;
449*ba07fd2fSBen Hoff 	bool interlace;
450*ba07fd2fSBen Hoff 
451*ba07fd2fSBen Hoff 	if (!edv)
452*ba07fd2fSBen Hoff 		return -EINVAL;
453*ba07fd2fSBen Hoff 
454*ba07fd2fSBen Hoff 	if (edv->pad)
455*ba07fd2fSBen Hoff 		return -EINVAL;
456*ba07fd2fSBen Hoff 
457*ba07fd2fSBen Hoff 	w = 0;
458*ba07fd2fSBen Hoff 	h = 0;
459*ba07fd2fSBen Hoff 	interlace = false;
460*ba07fd2fSBen Hoff 	if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) {
461*ba07fd2fSBen Hoff 		fps = hws_get_live_fps(vid);
462*ba07fd2fSBen Hoff 		if (!fps)
463*ba07fd2fSBen Hoff 			fps = vid->current_fps ? vid->current_fps :
464*ba07fd2fSBen Hoff 			      hws_pick_fps_from_mode(w, h, interlace);
465*ba07fd2fSBen Hoff 		m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL;
466*ba07fd2fSBen Hoff 		if (!m)
467*ba07fd2fSBen Hoff 			m = hws_find_dv_by_wh(w, h, interlace);
468*ba07fd2fSBen Hoff 		if (m) {
469*ba07fd2fSBen Hoff 			if (edv->index)
470*ba07fd2fSBen Hoff 				return -EINVAL;
471*ba07fd2fSBen Hoff 			edv->timings = m->timings;
472*ba07fd2fSBen Hoff 			return 0;
473*ba07fd2fSBen Hoff 		}
474*ba07fd2fSBen Hoff 	}
475*ba07fd2fSBen Hoff 
476*ba07fd2fSBen Hoff 	if (edv->index >= hws_dv_modes_cnt)
477*ba07fd2fSBen Hoff 		return -EINVAL;
478*ba07fd2fSBen Hoff 
479*ba07fd2fSBen Hoff 	edv->timings = hws_dv_modes[edv->index].timings;
480*ba07fd2fSBen Hoff 	return 0;
481*ba07fd2fSBen Hoff }
482*ba07fd2fSBen Hoff 
483*ba07fd2fSBen Hoff /* Get the *currently configured* DV timings. */
484*ba07fd2fSBen Hoff int hws_vidioc_g_dv_timings(struct file *file, void *fh,
485*ba07fd2fSBen Hoff 			    struct v4l2_dv_timings *timings)
486*ba07fd2fSBen Hoff {
487*ba07fd2fSBen Hoff 	struct hws_video *vid = video_drvdata(file);
488*ba07fd2fSBen Hoff 	u32 w, h;
489*ba07fd2fSBen Hoff 	u32 fps;
490*ba07fd2fSBen Hoff 	bool interlace;
491*ba07fd2fSBen Hoff 
492*ba07fd2fSBen Hoff 	if (!timings)
493*ba07fd2fSBen Hoff 		return -EINVAL;
494*ba07fd2fSBen Hoff 
495*ba07fd2fSBen Hoff 	w = vid->pix.width;
496*ba07fd2fSBen Hoff 	h = vid->pix.height;
497*ba07fd2fSBen Hoff 	interlace = vid->pix.interlaced;
498*ba07fd2fSBen Hoff 	if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) {
499*ba07fd2fSBen Hoff 		fps = hws_get_live_fps(vid);
500*ba07fd2fSBen Hoff 		if (!fps)
501*ba07fd2fSBen Hoff 			fps = vid->current_fps ? vid->current_fps :
502*ba07fd2fSBen Hoff 			      hws_pick_fps_from_mode(w, h, interlace);
503*ba07fd2fSBen Hoff 		return hws_fill_dv_timings(w, h, interlace, fps, timings);
504*ba07fd2fSBen Hoff 	}
505*ba07fd2fSBen Hoff 
506*ba07fd2fSBen Hoff 	*timings = vid->cur_dv_timings;
507*ba07fd2fSBen Hoff 	return 0;
508*ba07fd2fSBen Hoff }
509*ba07fd2fSBen Hoff 
510*ba07fd2fSBen Hoff static inline void hws_set_colorimetry_state(struct hws_pix_state *p)
511*ba07fd2fSBen Hoff {
512*ba07fd2fSBen Hoff 	bool sd = p->height <= 576;
513*ba07fd2fSBen Hoff 
514*ba07fd2fSBen Hoff 	p->colorspace   = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
515*ba07fd2fSBen Hoff 	p->ycbcr_enc    = V4L2_YCBCR_ENC_DEFAULT;
516*ba07fd2fSBen Hoff 	p->quantization = V4L2_QUANTIZATION_FULL_RANGE;
517*ba07fd2fSBen Hoff 	p->xfer_func    = V4L2_XFER_FUNC_DEFAULT;
518*ba07fd2fSBen Hoff }
519*ba07fd2fSBen Hoff 
520*ba07fd2fSBen Hoff /* Set DV timings: must match one of our supported modes.
521*ba07fd2fSBen Hoff  * If buffers are queued and this implies a size change, we reject with -EBUSY.
522*ba07fd2fSBen Hoff  * Otherwise we update pix state and (optionally) reprogram the HW.
523*ba07fd2fSBen Hoff  */
524*ba07fd2fSBen Hoff int hws_vidioc_s_dv_timings(struct file *file, void *fh,
525*ba07fd2fSBen Hoff 			    struct v4l2_dv_timings *timings)
526*ba07fd2fSBen Hoff {
527*ba07fd2fSBen Hoff 	struct hws_video *vid = video_drvdata(file);
528*ba07fd2fSBen Hoff 	const struct hws_dv_mode *m;
529*ba07fd2fSBen Hoff 	const struct v4l2_bt_timings *bt;
530*ba07fd2fSBen Hoff 	u32 new_w, new_h;
531*ba07fd2fSBen Hoff 	bool interlaced;
532*ba07fd2fSBen Hoff 	int ret = 0;
533*ba07fd2fSBen Hoff 	unsigned long was_busy;
534*ba07fd2fSBen Hoff 	u32 live_w, live_h;
535*ba07fd2fSBen Hoff 	u32 live_fps;
536*ba07fd2fSBen Hoff 	bool live_interlaced;
537*ba07fd2fSBen Hoff 	bool live_present;
538*ba07fd2fSBen Hoff 
539*ba07fd2fSBen Hoff 	if (!timings)
540*ba07fd2fSBen Hoff 		return -EINVAL;
541*ba07fd2fSBen Hoff 
542*ba07fd2fSBen Hoff 	m = hws_match_supported_dv(timings);
543*ba07fd2fSBen Hoff 	if (!m)
544*ba07fd2fSBen Hoff 		return -EINVAL;
545*ba07fd2fSBen Hoff 
546*ba07fd2fSBen Hoff 	bt = &m->timings.bt;
547*ba07fd2fSBen Hoff 	if (bt->interlaced)
548*ba07fd2fSBen Hoff 		return -EINVAL; /* only progressive modes are advertised */
549*ba07fd2fSBen Hoff 	new_w = bt->width;
550*ba07fd2fSBen Hoff 	new_h = bt->height;
551*ba07fd2fSBen Hoff 	interlaced = false;
552*ba07fd2fSBen Hoff 
553*ba07fd2fSBen Hoff 	lockdep_assert_held(&vid->state_lock);
554*ba07fd2fSBen Hoff 	live_present = hws_get_live_dv_geometry(vid, &live_w, &live_h,
555*ba07fd2fSBen Hoff 						&live_interlaced);
556*ba07fd2fSBen Hoff 
557*ba07fd2fSBen Hoff 	/* If vb2 has active buffers and size would change, reject. */
558*ba07fd2fSBen Hoff 	was_busy = vb2_is_busy(&vid->buffer_queue);
559*ba07fd2fSBen Hoff 	if (was_busy &&
560*ba07fd2fSBen Hoff 	    (new_w != vid->pix.width || new_h != vid->pix.height ||
561*ba07fd2fSBen Hoff 	     interlaced != vid->pix.interlaced)) {
562*ba07fd2fSBen Hoff 		ret = -EBUSY;
563*ba07fd2fSBen Hoff 		return ret;
564*ba07fd2fSBen Hoff 	}
565*ba07fd2fSBen Hoff 
566*ba07fd2fSBen Hoff 	/* When a live input signal is present, the receiver owns the timing.
567*ba07fd2fSBen Hoff 	 * Allow setting the already-active timings so v4l2-compliance can
568*ba07fd2fSBen Hoff 	 * round-trip them, but reject attempts to retime the live source.
569*ba07fd2fSBen Hoff 	 */
570*ba07fd2fSBen Hoff 	if (live_present) {
571*ba07fd2fSBen Hoff 		live_fps = hws_get_live_fps(vid);
572*ba07fd2fSBen Hoff 		if (!live_fps)
573*ba07fd2fSBen Hoff 			live_fps = vid->current_fps ? vid->current_fps :
574*ba07fd2fSBen Hoff 					hws_pick_fps_from_mode(live_w, live_h,
575*ba07fd2fSBen Hoff 							       live_interlaced);
576*ba07fd2fSBen Hoff 		if (live_w == new_w && live_h == new_h &&
577*ba07fd2fSBen Hoff 		    live_interlaced == interlaced &&
578*ba07fd2fSBen Hoff 		    m->refresh_hz == live_fps)
579*ba07fd2fSBen Hoff 			return 0;
580*ba07fd2fSBen Hoff 		return -EBUSY;
581*ba07fd2fSBen Hoff 	}
582*ba07fd2fSBen Hoff 
583*ba07fd2fSBen Hoff 	/* Update software pixel state (and recalc sizes) */
584*ba07fd2fSBen Hoff 	vid->pix.width      = new_w;
585*ba07fd2fSBen Hoff 	vid->pix.height     = new_h;
586*ba07fd2fSBen Hoff 	vid->pix.field      = interlaced ? V4L2_FIELD_INTERLACED
587*ba07fd2fSBen Hoff 					 : V4L2_FIELD_NONE;
588*ba07fd2fSBen Hoff 	vid->pix.interlaced = interlaced;
589*ba07fd2fSBen Hoff 	vid->pix.fourcc     = V4L2_PIX_FMT_YUYV;
590*ba07fd2fSBen Hoff 
591*ba07fd2fSBen Hoff 	hws_set_colorimetry_state(&vid->pix);
592*ba07fd2fSBen Hoff 
593*ba07fd2fSBen Hoff 	/* Recompute stride, sizeimage, and half_size. */
594*ba07fd2fSBen Hoff 	vid->pix.bytesperline = hws_calc_bpl_yuyv(new_w);
595*ba07fd2fSBen Hoff 	vid->pix.sizeimage    = hws_calc_size_yuyv(new_w, new_h);
596*ba07fd2fSBen Hoff 	vid->pix.half_size    = hws_calc_half_size(vid->pix.sizeimage);
597*ba07fd2fSBen Hoff 	vid->cur_dv_timings   = m->timings;
598*ba07fd2fSBen Hoff 	vid->current_fps      = m->refresh_hz;
599*ba07fd2fSBen Hoff 	return ret;
600*ba07fd2fSBen Hoff }
601*ba07fd2fSBen Hoff 
602*ba07fd2fSBen Hoff /* Report DV timings capability: advertise BT.656/1120 with
603*ba07fd2fSBen Hoff  * the min/max WxH derived from our table and basic progressive support.
604*ba07fd2fSBen Hoff  */
605*ba07fd2fSBen Hoff int hws_vidioc_dv_timings_cap(struct file *file, void *fh,
606*ba07fd2fSBen Hoff 			      struct v4l2_dv_timings_cap *cap)
607*ba07fd2fSBen Hoff {
608*ba07fd2fSBen Hoff 	u32 min_w = ~0U, min_h = ~0U;
609*ba07fd2fSBen Hoff 	u32 max_w = 0,       max_h = 0;
610*ba07fd2fSBen Hoff 	size_t i, n = 0;
611*ba07fd2fSBen Hoff 
612*ba07fd2fSBen Hoff 	if (!cap)
613*ba07fd2fSBen Hoff 		return -EINVAL;
614*ba07fd2fSBen Hoff 
615*ba07fd2fSBen Hoff 	memset(cap, 0, sizeof(*cap));
616*ba07fd2fSBen Hoff 	cap->type = V4L2_DV_BT_656_1120;
617*ba07fd2fSBen Hoff 
618*ba07fd2fSBen Hoff 	for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) {
619*ba07fd2fSBen Hoff 		const struct v4l2_bt_timings *bt = &hws_dv_modes[i].timings.bt;
620*ba07fd2fSBen Hoff 
621*ba07fd2fSBen Hoff 		if (hws_dv_modes[i].timings.type != V4L2_DV_BT_656_1120)
622*ba07fd2fSBen Hoff 			continue;
623*ba07fd2fSBen Hoff 		n++;
624*ba07fd2fSBen Hoff 
625*ba07fd2fSBen Hoff 		if (bt->width  < min_w)
626*ba07fd2fSBen Hoff 			min_w = bt->width;
627*ba07fd2fSBen Hoff 		if (bt->height < min_h)
628*ba07fd2fSBen Hoff 			min_h = bt->height;
629*ba07fd2fSBen Hoff 		if (bt->width  > max_w)
630*ba07fd2fSBen Hoff 			max_w = bt->width;
631*ba07fd2fSBen Hoff 		if (bt->height > max_h)
632*ba07fd2fSBen Hoff 			max_h = bt->height;
633*ba07fd2fSBen Hoff 	}
634*ba07fd2fSBen Hoff 
635*ba07fd2fSBen Hoff 	/* If the table was empty, fail gracefully. */
636*ba07fd2fSBen Hoff 	if (!n || min_w == U32_MAX)
637*ba07fd2fSBen Hoff 		return -ENODATA;
638*ba07fd2fSBen Hoff 
639*ba07fd2fSBen Hoff 	cap->bt.min_width  = min_w;
640*ba07fd2fSBen Hoff 	cap->bt.max_width  = max_w;
641*ba07fd2fSBen Hoff 	cap->bt.min_height = min_h;
642*ba07fd2fSBen Hoff 	cap->bt.max_height = max_h;
643*ba07fd2fSBen Hoff 
644*ba07fd2fSBen Hoff 	/* We support both CEA-861- and VESA-style modes in the list. */
645*ba07fd2fSBen Hoff 	cap->bt.standards =
646*ba07fd2fSBen Hoff 		V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT;
647*ba07fd2fSBen Hoff 
648*ba07fd2fSBen Hoff 	/* Only progressive modes are advertised. */
649*ba07fd2fSBen Hoff 	cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE;
650*ba07fd2fSBen Hoff 
651*ba07fd2fSBen Hoff 	/* Leave pixelclock/porch limits unconstrained (0) for now. */
652*ba07fd2fSBen Hoff 	return 0;
653*ba07fd2fSBen Hoff }
654*ba07fd2fSBen Hoff 
655*ba07fd2fSBen Hoff static int hws_s_ctrl(struct v4l2_ctrl *ctrl)
656*ba07fd2fSBen Hoff {
657*ba07fd2fSBen Hoff 	struct hws_video *vid =
658*ba07fd2fSBen Hoff 		container_of(ctrl->handler, struct hws_video, control_handler);
659*ba07fd2fSBen Hoff 	struct hws_pcie_dev *pdx = vid->parent;
660*ba07fd2fSBen Hoff 	bool program = false;
661*ba07fd2fSBen Hoff 
662*ba07fd2fSBen Hoff 	switch (ctrl->id) {
663*ba07fd2fSBen Hoff 	case V4L2_CID_BRIGHTNESS:
664*ba07fd2fSBen Hoff 		vid->current_brightness = ctrl->val;
665*ba07fd2fSBen Hoff 		program = true;
666*ba07fd2fSBen Hoff 		break;
667*ba07fd2fSBen Hoff 	case V4L2_CID_CONTRAST:
668*ba07fd2fSBen Hoff 		vid->current_contrast = ctrl->val;
669*ba07fd2fSBen Hoff 		program = true;
670*ba07fd2fSBen Hoff 		break;
671*ba07fd2fSBen Hoff 	case V4L2_CID_SATURATION:
672*ba07fd2fSBen Hoff 		vid->current_saturation = ctrl->val;
673*ba07fd2fSBen Hoff 		program = true;
674*ba07fd2fSBen Hoff 		break;
675*ba07fd2fSBen Hoff 	case V4L2_CID_HUE:
676*ba07fd2fSBen Hoff 		vid->current_hue = ctrl->val;
677*ba07fd2fSBen Hoff 		program = true;
678*ba07fd2fSBen Hoff 		break;
679*ba07fd2fSBen Hoff 	default:
680*ba07fd2fSBen Hoff 		return -EINVAL;
681*ba07fd2fSBen Hoff 	}
682*ba07fd2fSBen Hoff 
683*ba07fd2fSBen Hoff 	if (program) {
684*ba07fd2fSBen Hoff 		hws_hw_write_bchs(pdx, vid->channel_index,
685*ba07fd2fSBen Hoff 				  (u8)vid->current_brightness,
686*ba07fd2fSBen Hoff 				  (u8)vid->current_contrast,
687*ba07fd2fSBen Hoff 				  (u8)vid->current_hue,
688*ba07fd2fSBen Hoff 				  (u8)vid->current_saturation);
689*ba07fd2fSBen Hoff 	}
690*ba07fd2fSBen Hoff 	return 0;
691*ba07fd2fSBen Hoff }
692*ba07fd2fSBen Hoff 
693*ba07fd2fSBen Hoff const struct v4l2_ctrl_ops hws_ctrl_ops = {
694*ba07fd2fSBen Hoff 	.s_ctrl = hws_s_ctrl,
695*ba07fd2fSBen Hoff };
696*ba07fd2fSBen Hoff 
697*ba07fd2fSBen Hoff int hws_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
698*ba07fd2fSBen Hoff {
699*ba07fd2fSBen Hoff 	struct hws_video *vid = video_drvdata(file);
700*ba07fd2fSBen Hoff 	int vi_index = vid->channel_index + 1; /* keep it simple */
701*ba07fd2fSBen Hoff 
702*ba07fd2fSBen Hoff 	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
703*ba07fd2fSBen Hoff 	snprintf(cap->card, sizeof(cap->card),
704*ba07fd2fSBen Hoff 		 "AVMatrix HWS Capture %d", vi_index);
705*ba07fd2fSBen Hoff 	return 0;
706*ba07fd2fSBen Hoff }
707*ba07fd2fSBen Hoff 
708*ba07fd2fSBen Hoff int hws_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh, struct v4l2_fmtdesc *f)
709*ba07fd2fSBen Hoff {
710*ba07fd2fSBen Hoff 	if (f->index != 0)
711*ba07fd2fSBen Hoff 		return -EINVAL; /* only one format */
712*ba07fd2fSBen Hoff 
713*ba07fd2fSBen Hoff 	f->pixelformat = V4L2_PIX_FMT_YUYV;
714*ba07fd2fSBen Hoff 	return 0;
715*ba07fd2fSBen Hoff }
716*ba07fd2fSBen Hoff 
717*ba07fd2fSBen Hoff int hws_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
718*ba07fd2fSBen Hoff {
719*ba07fd2fSBen Hoff 	struct hws_video *vid = video_drvdata(file);
720*ba07fd2fSBen Hoff 
721*ba07fd2fSBen Hoff 	fmt->fmt.pix.width        = vid->pix.width;
722*ba07fd2fSBen Hoff 	fmt->fmt.pix.height       = vid->pix.height;
723*ba07fd2fSBen Hoff 	fmt->fmt.pix.pixelformat  = V4L2_PIX_FMT_YUYV;
724*ba07fd2fSBen Hoff 	fmt->fmt.pix.field        = vid->pix.field;
725*ba07fd2fSBen Hoff 	fmt->fmt.pix.bytesperline = vid->pix.bytesperline;
726*ba07fd2fSBen Hoff 	fmt->fmt.pix.sizeimage    = vid->pix.sizeimage;
727*ba07fd2fSBen Hoff 	fmt->fmt.pix.colorspace   = vid->pix.colorspace;
728*ba07fd2fSBen Hoff 	fmt->fmt.pix.ycbcr_enc    = vid->pix.ycbcr_enc;
729*ba07fd2fSBen Hoff 	fmt->fmt.pix.quantization = vid->pix.quantization;
730*ba07fd2fSBen Hoff 	fmt->fmt.pix.xfer_func    = vid->pix.xfer_func;
731*ba07fd2fSBen Hoff 	return 0;
732*ba07fd2fSBen Hoff }
733*ba07fd2fSBen Hoff 
734*ba07fd2fSBen Hoff static inline void hws_set_colorimetry_fmt(struct v4l2_pix_format *p)
735*ba07fd2fSBen Hoff {
736*ba07fd2fSBen Hoff 	bool sd = p->height <= 576;
737*ba07fd2fSBen Hoff 
738*ba07fd2fSBen Hoff 	p->colorspace   = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
739*ba07fd2fSBen Hoff 	p->ycbcr_enc    = V4L2_YCBCR_ENC_DEFAULT;
740*ba07fd2fSBen Hoff 	p->quantization = V4L2_QUANTIZATION_FULL_RANGE;
741*ba07fd2fSBen Hoff 	p->xfer_func    = V4L2_XFER_FUNC_DEFAULT;
742*ba07fd2fSBen Hoff }
743*ba07fd2fSBen Hoff 
744*ba07fd2fSBen Hoff int hws_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
745*ba07fd2fSBen Hoff {
746*ba07fd2fSBen Hoff 	struct hws_video *vid = file ? video_drvdata(file) : NULL;
747*ba07fd2fSBen Hoff 	struct hws_pcie_dev *pdev = vid ? vid->parent : NULL;
748*ba07fd2fSBen Hoff 	struct v4l2_pix_format *pix = &f->fmt.pix;
749*ba07fd2fSBen Hoff 	u32 req_w = pix->width, req_h = pix->height;
750*ba07fd2fSBen Hoff 	u32 w, h, min_bpl, bpl;
751*ba07fd2fSBen Hoff 	size_t size; /* wider than u32 for overflow check */
752*ba07fd2fSBen Hoff 	size_t max_frame = pdev ? pdev->max_hw_video_buf_sz : MAX_MM_VIDEO_SIZE;
753*ba07fd2fSBen Hoff 
754*ba07fd2fSBen Hoff 	/* Only YUYV */
755*ba07fd2fSBen Hoff 	pix->pixelformat = V4L2_PIX_FMT_YUYV;
756*ba07fd2fSBen Hoff 
757*ba07fd2fSBen Hoff 	/* Defaults then clamp */
758*ba07fd2fSBen Hoff 	w = (req_w ? req_w : 640);
759*ba07fd2fSBen Hoff 	h = (req_h ? req_h : 480);
760*ba07fd2fSBen Hoff 	if (w > MAX_VIDEO_HW_W)
761*ba07fd2fSBen Hoff 		w = MAX_VIDEO_HW_W;
762*ba07fd2fSBen Hoff 	if (h > MAX_VIDEO_HW_H)
763*ba07fd2fSBen Hoff 		h = MAX_VIDEO_HW_H;
764*ba07fd2fSBen Hoff 	if (!w)
765*ba07fd2fSBen Hoff 		w = 640; /* hard fallback in case macros are odd */
766*ba07fd2fSBen Hoff 	if (!h)
767*ba07fd2fSBen Hoff 		h = 480;
768*ba07fd2fSBen Hoff 
769*ba07fd2fSBen Hoff 	/* Field policy */
770*ba07fd2fSBen Hoff 	pix->field = V4L2_FIELD_NONE;
771*ba07fd2fSBen Hoff 
772*ba07fd2fSBen Hoff 	/* Stride policy for packed 16bpp, 64B align */
773*ba07fd2fSBen Hoff 	min_bpl = ALIGN(w * 2, 64);
774*ba07fd2fSBen Hoff 
775*ba07fd2fSBen Hoff 	/* Bound requested bpl to something sane, then align */
776*ba07fd2fSBen Hoff 	bpl = pix->bytesperline;
777*ba07fd2fSBen Hoff 	if (bpl < min_bpl) {
778*ba07fd2fSBen Hoff 		bpl = min_bpl;
779*ba07fd2fSBen Hoff 	} else {
780*ba07fd2fSBen Hoff 		/* Cap at 16x width to avoid silly values that overflow sizeimage */
781*ba07fd2fSBen Hoff 		u32 max_bpl = ALIGN(w * 2 * 16, 64);
782*ba07fd2fSBen Hoff 
783*ba07fd2fSBen Hoff 		if (bpl > max_bpl)
784*ba07fd2fSBen Hoff 			bpl = max_bpl;
785*ba07fd2fSBen Hoff 		bpl = ALIGN(bpl, 64);
786*ba07fd2fSBen Hoff 	}
787*ba07fd2fSBen Hoff 	if (h && max_frame) {
788*ba07fd2fSBen Hoff 		size_t max_bpl_hw = max_frame / h;
789*ba07fd2fSBen Hoff 
790*ba07fd2fSBen Hoff 		if (max_bpl_hw < min_bpl)
791*ba07fd2fSBen Hoff 			return -ERANGE;
792*ba07fd2fSBen Hoff 		max_bpl_hw = rounddown(max_bpl_hw, 64);
793*ba07fd2fSBen Hoff 		if (!max_bpl_hw)
794*ba07fd2fSBen Hoff 			return -ERANGE;
795*ba07fd2fSBen Hoff 		if (bpl > max_bpl_hw) {
796*ba07fd2fSBen Hoff 			if (pdev)
797*ba07fd2fSBen Hoff 				dev_dbg(&pdev->pdev->dev,
798*ba07fd2fSBen Hoff 					"try_fmt: clamp bpl %u -> %zu due to hw buf cap %zu\n",
799*ba07fd2fSBen Hoff 					bpl, max_bpl_hw, max_frame);
800*ba07fd2fSBen Hoff 			bpl = (u32)max_bpl_hw;
801*ba07fd2fSBen Hoff 		}
802*ba07fd2fSBen Hoff 	}
803*ba07fd2fSBen Hoff 	size = (size_t)bpl * (size_t)h;
804*ba07fd2fSBen Hoff 	if (size > max_frame)
805*ba07fd2fSBen Hoff 		return -ERANGE;
806*ba07fd2fSBen Hoff 
807*ba07fd2fSBen Hoff 	pix->width        = w;
808*ba07fd2fSBen Hoff 	pix->height       = h;
809*ba07fd2fSBen Hoff 	pix->bytesperline = bpl;
810*ba07fd2fSBen Hoff 	pix->sizeimage    = (u32)size; /* logical size, not page-aligned */
811*ba07fd2fSBen Hoff 
812*ba07fd2fSBen Hoff 	hws_set_colorimetry_fmt(pix);
813*ba07fd2fSBen Hoff 	if (pdev)
814*ba07fd2fSBen Hoff 		dev_dbg(&pdev->pdev->dev,
815*ba07fd2fSBen Hoff 			"try_fmt: w=%u h=%u bpl=%u size=%u field=%u\n",
816*ba07fd2fSBen Hoff 			pix->width, pix->height, pix->bytesperline,
817*ba07fd2fSBen Hoff 			pix->sizeimage, pix->field);
818*ba07fd2fSBen Hoff 	return 0;
819*ba07fd2fSBen Hoff }
820*ba07fd2fSBen Hoff 
821*ba07fd2fSBen Hoff int hws_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
822*ba07fd2fSBen Hoff {
823*ba07fd2fSBen Hoff 	struct hws_video *vid = video_drvdata(file);
824*ba07fd2fSBen Hoff 	int ret;
825*ba07fd2fSBen Hoff 
826*ba07fd2fSBen Hoff 	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
827*ba07fd2fSBen Hoff 		return -EINVAL;
828*ba07fd2fSBen Hoff 
829*ba07fd2fSBen Hoff 	/* Normalize the request */
830*ba07fd2fSBen Hoff 	ret = hws_vidioc_try_fmt_vid_cap(file, priv, f);
831*ba07fd2fSBen Hoff 	if (ret)
832*ba07fd2fSBen Hoff 		return ret;
833*ba07fd2fSBen Hoff 
834*ba07fd2fSBen Hoff 	/* Don't allow buffer layout changes while buffers are queued. */
835*ba07fd2fSBen Hoff 	if (vb2_is_busy(&vid->buffer_queue)) {
836*ba07fd2fSBen Hoff 		if (f->fmt.pix.width  != vid->pix.width  ||
837*ba07fd2fSBen Hoff 		    f->fmt.pix.height != vid->pix.height ||
838*ba07fd2fSBen Hoff 		    f->fmt.pix.bytesperline != vid->pix.bytesperline)
839*ba07fd2fSBen Hoff 			return -EBUSY;
840*ba07fd2fSBen Hoff 	}
841*ba07fd2fSBen Hoff 
842*ba07fd2fSBen Hoff 	/* Apply to driver state */
843*ba07fd2fSBen Hoff 	vid->pix.width        = f->fmt.pix.width;
844*ba07fd2fSBen Hoff 	vid->pix.height       = f->fmt.pix.height;
845*ba07fd2fSBen Hoff 	vid->pix.fourcc       = V4L2_PIX_FMT_YUYV;
846*ba07fd2fSBen Hoff 	vid->pix.field        = f->fmt.pix.field;
847*ba07fd2fSBen Hoff 	vid->pix.colorspace   = f->fmt.pix.colorspace;
848*ba07fd2fSBen Hoff 	vid->pix.ycbcr_enc    = f->fmt.pix.ycbcr_enc;
849*ba07fd2fSBen Hoff 	vid->pix.quantization = f->fmt.pix.quantization;
850*ba07fd2fSBen Hoff 	vid->pix.xfer_func    = f->fmt.pix.xfer_func;
851*ba07fd2fSBen Hoff 
852*ba07fd2fSBen Hoff 	/* Update negotiated buffer sizes. */
853*ba07fd2fSBen Hoff 	vid->pix.bytesperline = f->fmt.pix.bytesperline; /* aligned */
854*ba07fd2fSBen Hoff 	vid->pix.sizeimage    = f->fmt.pix.sizeimage;    /* logical */
855*ba07fd2fSBen Hoff 	vid->pix.half_size    = hws_calc_half_size(vid->pix.sizeimage);
856*ba07fd2fSBen Hoff 	vid->pix.interlaced   = false;
857*ba07fd2fSBen Hoff 	/* S_FMT negotiates buffer layout only. Keep detector-owned DV timing
858*ba07fd2fSBen Hoff 	 * state unchanged so a harmless restart cannot clobber the live FPS.
859*ba07fd2fSBen Hoff 	 */
860*ba07fd2fSBen Hoff 	/* Or:
861*ba07fd2fSBen Hoff 	 * hws_calc_sizeimage(vid, vid->pix.width, vid->pix.height, false);
862*ba07fd2fSBen Hoff 	 */
863*ba07fd2fSBen Hoff 
864*ba07fd2fSBen Hoff 	dev_dbg(&vid->parent->pdev->dev,
865*ba07fd2fSBen Hoff 		"s_fmt:   w=%u h=%u bpl=%u size=%u\n",
866*ba07fd2fSBen Hoff 		vid->pix.width, vid->pix.height, vid->pix.bytesperline,
867*ba07fd2fSBen Hoff 		vid->pix.sizeimage);
868*ba07fd2fSBen Hoff 
869*ba07fd2fSBen Hoff 	return 0;
870*ba07fd2fSBen Hoff }
871*ba07fd2fSBen Hoff 
872*ba07fd2fSBen Hoff int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *param)
873*ba07fd2fSBen Hoff {
874*ba07fd2fSBen Hoff 	struct hws_video *vid = video_drvdata(file);
875*ba07fd2fSBen Hoff 	u32 fps;
876*ba07fd2fSBen Hoff 
877*ba07fd2fSBen Hoff 	if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
878*ba07fd2fSBen Hoff 		return -EINVAL;
879*ba07fd2fSBen Hoff 
880*ba07fd2fSBen Hoff 	fps = hws_get_live_fps(vid);
881*ba07fd2fSBen Hoff 	if (!fps)
882*ba07fd2fSBen Hoff 		fps = vid->current_fps ? vid->current_fps : 60;
883*ba07fd2fSBen Hoff 
884*ba07fd2fSBen Hoff 	/* HDMI receivers report the detected frame period, they don't set it. */
885*ba07fd2fSBen Hoff 	param->parm.capture.capability           = 0;
886*ba07fd2fSBen Hoff 	param->parm.capture.capturemode          = 0;
887*ba07fd2fSBen Hoff 	param->parm.capture.timeperframe.numerator   = 1;
888*ba07fd2fSBen Hoff 	param->parm.capture.timeperframe.denominator = fps;
889*ba07fd2fSBen Hoff 	param->parm.capture.extendedmode         = 0;
890*ba07fd2fSBen Hoff 	param->parm.capture.readbuffers          = 0;
891*ba07fd2fSBen Hoff 
892*ba07fd2fSBen Hoff 	return 0;
893*ba07fd2fSBen Hoff }
894*ba07fd2fSBen Hoff 
895*ba07fd2fSBen Hoff int hws_vidioc_enum_input(struct file *file, void *priv,
896*ba07fd2fSBen Hoff 			  struct v4l2_input *input)
897*ba07fd2fSBen Hoff {
898*ba07fd2fSBen Hoff 	struct hws_video *vid = video_drvdata(file);
899*ba07fd2fSBen Hoff 
900*ba07fd2fSBen Hoff 	if (input->index)
901*ba07fd2fSBen Hoff 		return -EINVAL;
902*ba07fd2fSBen Hoff 	input->type         = V4L2_INPUT_TYPE_CAMERA;
903*ba07fd2fSBen Hoff 	strscpy(input->name, KBUILD_MODNAME, sizeof(input->name));
904*ba07fd2fSBen Hoff 	input->capabilities = V4L2_IN_CAP_DV_TIMINGS;
905*ba07fd2fSBen Hoff 	input->status       = hws_input_status(vid);
906*ba07fd2fSBen Hoff 
907*ba07fd2fSBen Hoff 	return 0;
908*ba07fd2fSBen Hoff }
909*ba07fd2fSBen Hoff 
910*ba07fd2fSBen Hoff int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *index)
911*ba07fd2fSBen Hoff {
912*ba07fd2fSBen Hoff 	*index = 0;
913*ba07fd2fSBen Hoff 	return 0;
914*ba07fd2fSBen Hoff }
915*ba07fd2fSBen Hoff 
916*ba07fd2fSBen Hoff int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i)
917*ba07fd2fSBen Hoff {
918*ba07fd2fSBen Hoff 	return i ? -EINVAL : 0;
919*ba07fd2fSBen Hoff }
920