xref: /linux/drivers/media/pci/hws/hws_video.c (revision 3d5e48944e824bddc20d7b874e784f7b279636fe)
1*ba07fd2fSBen Hoff // SPDX-License-Identifier: GPL-2.0-only
2*ba07fd2fSBen Hoff #include <linux/pci.h>
3*ba07fd2fSBen Hoff #include <linux/errno.h>
4*ba07fd2fSBen Hoff #include <linux/kernel.h>
5*ba07fd2fSBen Hoff #include <linux/compiler.h>
6*ba07fd2fSBen Hoff #include <linux/overflow.h>
7*ba07fd2fSBen Hoff #include <linux/delay.h>
8*ba07fd2fSBen Hoff #include <linux/bits.h>
9*ba07fd2fSBen Hoff #include <linux/jiffies.h>
10*ba07fd2fSBen Hoff #include <linux/ktime.h>
11*ba07fd2fSBen Hoff #include <linux/math64.h>
12*ba07fd2fSBen Hoff #include <linux/interrupt.h>
13*ba07fd2fSBen Hoff #include <linux/moduleparam.h>
14*ba07fd2fSBen Hoff 
15*ba07fd2fSBen Hoff #include <media/v4l2-ioctl.h>
16*ba07fd2fSBen Hoff #include <media/v4l2-ctrls.h>
17*ba07fd2fSBen Hoff #include <media/v4l2-dev.h>
18*ba07fd2fSBen Hoff #include <media/v4l2-event.h>
19*ba07fd2fSBen Hoff #include <media/videobuf2-v4l2.h>
20*ba07fd2fSBen Hoff #include <media/v4l2-device.h>
21*ba07fd2fSBen Hoff #include <media/videobuf2-dma-contig.h>
22*ba07fd2fSBen Hoff 
23*ba07fd2fSBen Hoff #include "hws.h"
24*ba07fd2fSBen Hoff #include "hws_reg.h"
25*ba07fd2fSBen Hoff #include "hws_video.h"
26*ba07fd2fSBen Hoff #include "hws_irq.h"
27*ba07fd2fSBen Hoff #include "hws_v4l2_ioctl.h"
28*ba07fd2fSBen Hoff 
29*ba07fd2fSBen Hoff #define HWS_REMAP_SLOT_OFF(ch)   (0x208 + (ch) * 8)	/* one 64-bit slot per ch */
30*ba07fd2fSBen Hoff #define HWS_BUF_BASE_OFF(ch)     (CVBS_IN_BUF_BASE  + (ch) * PCIE_BARADDROFSIZE)
31*ba07fd2fSBen Hoff #define HWS_HALF_SZ_OFF(ch)      (CVBS_IN_BUF_BASE2 + (ch) * PCIE_BARADDROFSIZE)
32*ba07fd2fSBen Hoff 
33*ba07fd2fSBen Hoff static void update_live_resolution(struct hws_pcie_dev *pdx, unsigned int ch,
34*ba07fd2fSBen Hoff 				   bool interlace);
35*ba07fd2fSBen Hoff static bool hws_read_active_state(struct hws_pcie_dev *pdx, unsigned int ch,
36*ba07fd2fSBen Hoff 				  bool *interlace);
37*ba07fd2fSBen Hoff static void handle_hwv2_path(struct hws_pcie_dev *hws, unsigned int ch);
38*ba07fd2fSBen Hoff static void handle_legacy_path(struct hws_pcie_dev *hws, unsigned int ch);
39*ba07fd2fSBen Hoff static u32 hws_calc_sizeimage(struct hws_video *v, u16 w, u16 h,
40*ba07fd2fSBen Hoff 			      bool interlaced);
41*ba07fd2fSBen Hoff 
42*ba07fd2fSBen Hoff /* DMA helper functions */
43*ba07fd2fSBen Hoff static void hws_program_dma_window(struct hws_video *vid, dma_addr_t dma);
44*ba07fd2fSBen Hoff static struct hwsvideo_buffer *
45*ba07fd2fSBen Hoff hws_take_queued_buffer_locked(struct hws_video *vid);
46*ba07fd2fSBen Hoff 
47*ba07fd2fSBen Hoff static unsigned long long hws_elapsed_us(u64 start_ns)
48*ba07fd2fSBen Hoff {
49*ba07fd2fSBen Hoff 	return div_u64(ktime_get_mono_fast_ns() - start_ns, 1000);
50*ba07fd2fSBen Hoff }
51*ba07fd2fSBen Hoff 
52*ba07fd2fSBen Hoff static inline bool list_node_unlinked(const struct list_head *n)
53*ba07fd2fSBen Hoff {
54*ba07fd2fSBen Hoff 	return n->next == LIST_POISON1 || n->prev == LIST_POISON2;
55*ba07fd2fSBen Hoff }
56*ba07fd2fSBen Hoff 
57*ba07fd2fSBen Hoff static bool dma_window_verify;
58*ba07fd2fSBen Hoff module_param_named(dma_window_verify, dma_window_verify, bool, 0644);
59*ba07fd2fSBen Hoff MODULE_PARM_DESC(dma_window_verify,
60*ba07fd2fSBen Hoff 		 "Read back DMA window registers after programming (debug)");
61*ba07fd2fSBen Hoff 
62*ba07fd2fSBen Hoff void hws_set_dma_doorbell(struct hws_pcie_dev *hws, unsigned int ch,
63*ba07fd2fSBen Hoff 			  dma_addr_t dma, const char *tag)
64*ba07fd2fSBen Hoff {
65*ba07fd2fSBen Hoff 	iowrite32(lower_32_bits(dma), hws->bar0_base + HWS_REG_DMA_ADDR(ch));
66*ba07fd2fSBen Hoff 	dev_dbg(&hws->pdev->dev, "dma_doorbell ch%u: dma=0x%llx tag=%s\n", ch,
67*ba07fd2fSBen Hoff 		(u64)dma, tag ? tag : "");
68*ba07fd2fSBen Hoff }
69*ba07fd2fSBen Hoff 
70*ba07fd2fSBen Hoff static void hws_program_dma_window(struct hws_video *vid, dma_addr_t dma)
71*ba07fd2fSBen Hoff {
72*ba07fd2fSBen Hoff 	const u32 addr_mask = PCI_E_BAR_ADD_MASK;
73*ba07fd2fSBen Hoff 	const u32 addr_low_mask = PCI_E_BAR_ADD_LOWMASK;
74*ba07fd2fSBen Hoff 	struct hws_pcie_dev *hws = vid->parent;
75*ba07fd2fSBen Hoff 	unsigned int ch = vid->channel_index;
76*ba07fd2fSBen Hoff 	u32 table_off = HWS_REMAP_SLOT_OFF(ch);
77*ba07fd2fSBen Hoff 	u32 lo = lower_32_bits(dma);
78*ba07fd2fSBen Hoff 	u32 hi = upper_32_bits(dma);
79*ba07fd2fSBen Hoff 	u32 pci_addr = lo & addr_low_mask;
80*ba07fd2fSBen Hoff 	u32 page_lo = lo & addr_mask;
81*ba07fd2fSBen Hoff 
82*ba07fd2fSBen Hoff 	bool wrote = false;
83*ba07fd2fSBen Hoff 
84*ba07fd2fSBen Hoff 	/* Remap entry only when DMA crosses into a new 512 MB page */
85*ba07fd2fSBen Hoff 	if (!vid->window_valid || vid->last_dma_hi != hi ||
86*ba07fd2fSBen Hoff 	    vid->last_dma_page != page_lo) {
87*ba07fd2fSBen Hoff 		writel(hi, hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off);
88*ba07fd2fSBen Hoff 		writel(page_lo,
89*ba07fd2fSBen Hoff 		       hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off +
90*ba07fd2fSBen Hoff 		       PCIE_BARADDROFSIZE);
91*ba07fd2fSBen Hoff 		vid->last_dma_hi = hi;
92*ba07fd2fSBen Hoff 		vid->last_dma_page = page_lo;
93*ba07fd2fSBen Hoff 		wrote = true;
94*ba07fd2fSBen Hoff 	}
95*ba07fd2fSBen Hoff 
96*ba07fd2fSBen Hoff 	/* Base pointer only needs low 29 bits */
97*ba07fd2fSBen Hoff 	if (!vid->window_valid || vid->last_pci_addr != pci_addr) {
98*ba07fd2fSBen Hoff 		writel((ch + 1) * PCIEBAR_AXI_BASE + pci_addr,
99*ba07fd2fSBen Hoff 		       hws->bar0_base + HWS_BUF_BASE_OFF(ch));
100*ba07fd2fSBen Hoff 		vid->last_pci_addr = pci_addr;
101*ba07fd2fSBen Hoff 		wrote = true;
102*ba07fd2fSBen Hoff 	}
103*ba07fd2fSBen Hoff 
104*ba07fd2fSBen Hoff 	/* Half-size only changes when resolution changes */
105*ba07fd2fSBen Hoff 	if (!vid->window_valid || vid->last_half16 != vid->pix.half_size / 16) {
106*ba07fd2fSBen Hoff 		writel(vid->pix.half_size / 16,
107*ba07fd2fSBen Hoff 		       hws->bar0_base + HWS_HALF_SZ_OFF(ch));
108*ba07fd2fSBen Hoff 		vid->last_half16 = vid->pix.half_size / 16;
109*ba07fd2fSBen Hoff 		wrote = true;
110*ba07fd2fSBen Hoff 	}
111*ba07fd2fSBen Hoff 
112*ba07fd2fSBen Hoff 	vid->window_valid = true;
113*ba07fd2fSBen Hoff 
114*ba07fd2fSBen Hoff 	if (dma_window_verify && wrote) {
115*ba07fd2fSBen Hoff 		u32 r_hi =
116*ba07fd2fSBen Hoff 		    readl(hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off);
117*ba07fd2fSBen Hoff 		u32 r_lo =
118*ba07fd2fSBen Hoff 		    readl(hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off +
119*ba07fd2fSBen Hoff 			  PCIE_BARADDROFSIZE);
120*ba07fd2fSBen Hoff 		u32 r_base = readl(hws->bar0_base + HWS_BUF_BASE_OFF(ch));
121*ba07fd2fSBen Hoff 		u32 r_half = readl(hws->bar0_base + HWS_HALF_SZ_OFF(ch));
122*ba07fd2fSBen Hoff 
123*ba07fd2fSBen Hoff 		dev_dbg(&hws->pdev->dev,
124*ba07fd2fSBen Hoff 			"ch%u remap verify: hi=0x%08x page_lo=0x%08x exp_page=0x%08x base=0x%08x exp_base=0x%08x half16B=0x%08x exp_half=0x%08x\n",
125*ba07fd2fSBen Hoff 			ch, r_hi, r_lo, page_lo, r_base,
126*ba07fd2fSBen Hoff 			(ch + 1) * PCIEBAR_AXI_BASE + pci_addr, r_half,
127*ba07fd2fSBen Hoff 			vid->pix.half_size / 16);
128*ba07fd2fSBen Hoff 	} else if (wrote) {
129*ba07fd2fSBen Hoff 		/* Flush posted writes before arming DMA */
130*ba07fd2fSBen Hoff 		readl_relaxed(hws->bar0_base + HWS_HALF_SZ_OFF(ch));
131*ba07fd2fSBen Hoff 	}
132*ba07fd2fSBen Hoff }
133*ba07fd2fSBen Hoff 
134*ba07fd2fSBen Hoff static struct hwsvideo_buffer *
135*ba07fd2fSBen Hoff hws_take_queued_buffer_locked(struct hws_video *vid)
136*ba07fd2fSBen Hoff {
137*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *buf;
138*ba07fd2fSBen Hoff 
139*ba07fd2fSBen Hoff 	if (!vid || list_empty(&vid->capture_queue))
140*ba07fd2fSBen Hoff 		return NULL;
141*ba07fd2fSBen Hoff 
142*ba07fd2fSBen Hoff 	buf = list_first_entry(&vid->capture_queue,
143*ba07fd2fSBen Hoff 			       struct hwsvideo_buffer, list);
144*ba07fd2fSBen Hoff 	list_del_init(&buf->list);
145*ba07fd2fSBen Hoff 	if (vid->queued_count)
146*ba07fd2fSBen Hoff 		vid->queued_count--;
147*ba07fd2fSBen Hoff 	return buf;
148*ba07fd2fSBen Hoff }
149*ba07fd2fSBen Hoff 
150*ba07fd2fSBen Hoff void hws_prime_next_locked(struct hws_video *vid)
151*ba07fd2fSBen Hoff {
152*ba07fd2fSBen Hoff 	struct hws_pcie_dev *hws;
153*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *next;
154*ba07fd2fSBen Hoff 	dma_addr_t dma;
155*ba07fd2fSBen Hoff 
156*ba07fd2fSBen Hoff 	if (!vid)
157*ba07fd2fSBen Hoff 		return;
158*ba07fd2fSBen Hoff 
159*ba07fd2fSBen Hoff 	hws = vid->parent;
160*ba07fd2fSBen Hoff 	if (!hws || !hws->bar0_base)
161*ba07fd2fSBen Hoff 		return;
162*ba07fd2fSBen Hoff 
163*ba07fd2fSBen Hoff 	if (!READ_ONCE(vid->cap_active) || !vid->active || vid->next_prepared)
164*ba07fd2fSBen Hoff 		return;
165*ba07fd2fSBen Hoff 
166*ba07fd2fSBen Hoff 	next = hws_take_queued_buffer_locked(vid);
167*ba07fd2fSBen Hoff 	if (!next)
168*ba07fd2fSBen Hoff 		return;
169*ba07fd2fSBen Hoff 
170*ba07fd2fSBen Hoff 	vid->next_prepared = next;
171*ba07fd2fSBen Hoff 	dma = vb2_dma_contig_plane_dma_addr(&next->vb.vb2_buf, 0);
172*ba07fd2fSBen Hoff 	hws_program_dma_for_addr(hws, vid->channel_index, dma);
173*ba07fd2fSBen Hoff 	iowrite32(lower_32_bits(dma),
174*ba07fd2fSBen Hoff 		  hws->bar0_base + HWS_REG_DMA_ADDR(vid->channel_index));
175*ba07fd2fSBen Hoff 	dev_dbg(&hws->pdev->dev,
176*ba07fd2fSBen Hoff 		"ch%u pre-armed next buffer %p dma=0x%llx\n",
177*ba07fd2fSBen Hoff 		vid->channel_index, next, (u64)dma);
178*ba07fd2fSBen Hoff }
179*ba07fd2fSBen Hoff 
180*ba07fd2fSBen Hoff static bool hws_force_no_signal_frame(struct hws_video *v, const char *tag)
181*ba07fd2fSBen Hoff {
182*ba07fd2fSBen Hoff 	struct hws_pcie_dev *hws;
183*ba07fd2fSBen Hoff 	unsigned long flags;
184*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *buf = NULL, *next = NULL;
185*ba07fd2fSBen Hoff 	bool have_next = false;
186*ba07fd2fSBen Hoff 	bool doorbell = false;
187*ba07fd2fSBen Hoff 
188*ba07fd2fSBen Hoff 	if (!v)
189*ba07fd2fSBen Hoff 		return false;
190*ba07fd2fSBen Hoff 	hws = v->parent;
191*ba07fd2fSBen Hoff 	if (!hws || READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active))
192*ba07fd2fSBen Hoff 		return false;
193*ba07fd2fSBen Hoff 	spin_lock_irqsave(&v->irq_lock, flags);
194*ba07fd2fSBen Hoff 	if (v->active) {
195*ba07fd2fSBen Hoff 		buf = v->active;
196*ba07fd2fSBen Hoff 		v->active = NULL;
197*ba07fd2fSBen Hoff 		buf->slot = 0;
198*ba07fd2fSBen Hoff 	} else if (!list_empty(&v->capture_queue)) {
199*ba07fd2fSBen Hoff 		buf = list_first_entry(&v->capture_queue,
200*ba07fd2fSBen Hoff 				       struct hwsvideo_buffer, list);
201*ba07fd2fSBen Hoff 		list_del_init(&buf->list);
202*ba07fd2fSBen Hoff 		if (v->queued_count)
203*ba07fd2fSBen Hoff 			v->queued_count--;
204*ba07fd2fSBen Hoff 		buf->slot = 0;
205*ba07fd2fSBen Hoff 	}
206*ba07fd2fSBen Hoff 	if (v->next_prepared) {
207*ba07fd2fSBen Hoff 		next = v->next_prepared;
208*ba07fd2fSBen Hoff 		v->next_prepared = NULL;
209*ba07fd2fSBen Hoff 		next->slot = 0;
210*ba07fd2fSBen Hoff 		v->active = next;
211*ba07fd2fSBen Hoff 		have_next = true;
212*ba07fd2fSBen Hoff 	} else if (!list_empty(&v->capture_queue)) {
213*ba07fd2fSBen Hoff 		next = list_first_entry(&v->capture_queue,
214*ba07fd2fSBen Hoff 					struct hwsvideo_buffer, list);
215*ba07fd2fSBen Hoff 		list_del_init(&next->list);
216*ba07fd2fSBen Hoff 		if (v->queued_count)
217*ba07fd2fSBen Hoff 			v->queued_count--;
218*ba07fd2fSBen Hoff 		next->slot = 0;
219*ba07fd2fSBen Hoff 		v->active = next;
220*ba07fd2fSBen Hoff 		have_next = true;
221*ba07fd2fSBen Hoff 	} else {
222*ba07fd2fSBen Hoff 		v->active = NULL;
223*ba07fd2fSBen Hoff 	}
224*ba07fd2fSBen Hoff 	spin_unlock_irqrestore(&v->irq_lock, flags);
225*ba07fd2fSBen Hoff 	if (!buf)
226*ba07fd2fSBen Hoff 		return false;
227*ba07fd2fSBen Hoff 	/* Complete buffer with a neutral frame so dequeuers keep running. */
228*ba07fd2fSBen Hoff 	{
229*ba07fd2fSBen Hoff 		struct vb2_v4l2_buffer *vb2v = &buf->vb;
230*ba07fd2fSBen Hoff 		void *dst = vb2_plane_vaddr(&vb2v->vb2_buf, 0);
231*ba07fd2fSBen Hoff 
232*ba07fd2fSBen Hoff 		if (dst)
233*ba07fd2fSBen Hoff 			memset(dst, 0x10, v->pix.sizeimage);
234*ba07fd2fSBen Hoff 		vb2_set_plane_payload(&vb2v->vb2_buf, 0, v->pix.sizeimage);
235*ba07fd2fSBen Hoff 		vb2v->sequence = (u32)atomic_inc_return(&v->sequence_number);
236*ba07fd2fSBen Hoff 		vb2v->vb2_buf.timestamp = ktime_get_ns();
237*ba07fd2fSBen Hoff 		vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_DONE);
238*ba07fd2fSBen Hoff 	}
239*ba07fd2fSBen Hoff 	if (have_next && next) {
240*ba07fd2fSBen Hoff 		dma_addr_t dma =
241*ba07fd2fSBen Hoff 		    vb2_dma_contig_plane_dma_addr(&next->vb.vb2_buf, 0);
242*ba07fd2fSBen Hoff 		hws_program_dma_for_addr(hws, v->channel_index, dma);
243*ba07fd2fSBen Hoff 		hws_set_dma_doorbell(hws, v->channel_index, dma,
244*ba07fd2fSBen Hoff 				     tag ? tag : "nosignal_zero");
245*ba07fd2fSBen Hoff 		doorbell = true;
246*ba07fd2fSBen Hoff 	}
247*ba07fd2fSBen Hoff 	if (doorbell) {
248*ba07fd2fSBen Hoff 		wmb(); /* ensure descriptors visible before enabling capture */
249*ba07fd2fSBen Hoff 		hws_enable_video_capture(hws, v->channel_index, true);
250*ba07fd2fSBen Hoff 	}
251*ba07fd2fSBen Hoff 	return true;
252*ba07fd2fSBen Hoff }
253*ba07fd2fSBen Hoff 
254*ba07fd2fSBen Hoff static int hws_ctrls_init(struct hws_video *vid)
255*ba07fd2fSBen Hoff {
256*ba07fd2fSBen Hoff 	struct v4l2_ctrl_handler *hdl = &vid->control_handler;
257*ba07fd2fSBen Hoff 
258*ba07fd2fSBen Hoff 	/* Create BCHS controls. */
259*ba07fd2fSBen Hoff 	v4l2_ctrl_handler_init(hdl, 4);
260*ba07fd2fSBen Hoff 
261*ba07fd2fSBen Hoff 	vid->ctrl_brightness = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops,
262*ba07fd2fSBen Hoff 						 V4L2_CID_BRIGHTNESS,
263*ba07fd2fSBen Hoff 						 MIN_VAMP_BRIGHTNESS_UNITS,
264*ba07fd2fSBen Hoff 						 MAX_VAMP_BRIGHTNESS_UNITS, 1,
265*ba07fd2fSBen Hoff 						 HWS_BRIGHTNESS_DEFAULT);
266*ba07fd2fSBen Hoff 
267*ba07fd2fSBen Hoff 	vid->ctrl_contrast =
268*ba07fd2fSBen Hoff 	    v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, V4L2_CID_CONTRAST,
269*ba07fd2fSBen Hoff 			      MIN_VAMP_CONTRAST_UNITS, MAX_VAMP_CONTRAST_UNITS,
270*ba07fd2fSBen Hoff 			      1, HWS_CONTRAST_DEFAULT);
271*ba07fd2fSBen Hoff 
272*ba07fd2fSBen Hoff 	vid->ctrl_saturation = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops,
273*ba07fd2fSBen Hoff 						 V4L2_CID_SATURATION,
274*ba07fd2fSBen Hoff 						 MIN_VAMP_SATURATION_UNITS,
275*ba07fd2fSBen Hoff 						 MAX_VAMP_SATURATION_UNITS, 1,
276*ba07fd2fSBen Hoff 						 HWS_SATURATION_DEFAULT);
277*ba07fd2fSBen Hoff 
278*ba07fd2fSBen Hoff 	vid->ctrl_hue = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, V4L2_CID_HUE,
279*ba07fd2fSBen Hoff 					  MIN_VAMP_HUE_UNITS,
280*ba07fd2fSBen Hoff 					  MAX_VAMP_HUE_UNITS, 1,
281*ba07fd2fSBen Hoff 					  HWS_HUE_DEFAULT);
282*ba07fd2fSBen Hoff 	if (hdl->error) {
283*ba07fd2fSBen Hoff 		int err = hdl->error;
284*ba07fd2fSBen Hoff 
285*ba07fd2fSBen Hoff 		v4l2_ctrl_handler_free(hdl);
286*ba07fd2fSBen Hoff 		return err;
287*ba07fd2fSBen Hoff 	}
288*ba07fd2fSBen Hoff 	return 0;
289*ba07fd2fSBen Hoff }
290*ba07fd2fSBen Hoff 
291*ba07fd2fSBen Hoff int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch)
292*ba07fd2fSBen Hoff {
293*ba07fd2fSBen Hoff 	struct hws_video *vid;
294*ba07fd2fSBen Hoff 
295*ba07fd2fSBen Hoff 	/* basic sanity */
296*ba07fd2fSBen Hoff 	if (!pdev || ch < 0 || ch >= pdev->max_channels)
297*ba07fd2fSBen Hoff 		return -EINVAL;
298*ba07fd2fSBen Hoff 
299*ba07fd2fSBen Hoff 	vid = &pdev->video[ch];
300*ba07fd2fSBen Hoff 
301*ba07fd2fSBen Hoff 	/* hard reset the per-channel struct (safe here since we init everything next) */
302*ba07fd2fSBen Hoff 	memset(vid, 0, sizeof(*vid));
303*ba07fd2fSBen Hoff 
304*ba07fd2fSBen Hoff 	/* identity */
305*ba07fd2fSBen Hoff 	vid->parent = pdev;
306*ba07fd2fSBen Hoff 	vid->channel_index = ch;
307*ba07fd2fSBen Hoff 
308*ba07fd2fSBen Hoff 	/* locks & lists */
309*ba07fd2fSBen Hoff 	mutex_init(&vid->state_lock);
310*ba07fd2fSBen Hoff 	spin_lock_init(&vid->irq_lock);
311*ba07fd2fSBen Hoff 	INIT_LIST_HEAD(&vid->capture_queue);
312*ba07fd2fSBen Hoff 	atomic_set(&vid->sequence_number, 0);
313*ba07fd2fSBen Hoff 	vid->active = NULL;
314*ba07fd2fSBen Hoff 
315*ba07fd2fSBen Hoff 	/* DMA watchdog removed; retain counters for diagnostics */
316*ba07fd2fSBen Hoff 	vid->timeout_count = 0;
317*ba07fd2fSBen Hoff 	vid->error_count = 0;
318*ba07fd2fSBen Hoff 
319*ba07fd2fSBen Hoff 	vid->queued_count = 0;
320*ba07fd2fSBen Hoff 	vid->window_valid = false;
321*ba07fd2fSBen Hoff 
322*ba07fd2fSBen Hoff 	/* Default format. */
323*ba07fd2fSBen Hoff 	vid->pix.width = 1920;
324*ba07fd2fSBen Hoff 	vid->pix.height = 1080;
325*ba07fd2fSBen Hoff 	vid->pix.fourcc = V4L2_PIX_FMT_YUYV;
326*ba07fd2fSBen Hoff 	vid->pix.bytesperline = ALIGN(vid->pix.width * 2, 64);
327*ba07fd2fSBen Hoff 	vid->pix.sizeimage = vid->pix.bytesperline * vid->pix.height;
328*ba07fd2fSBen Hoff 	vid->pix.field = V4L2_FIELD_NONE;
329*ba07fd2fSBen Hoff 	vid->pix.colorspace = V4L2_COLORSPACE_REC709;
330*ba07fd2fSBen Hoff 	vid->pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
331*ba07fd2fSBen Hoff 	vid->pix.quantization = V4L2_QUANTIZATION_FULL_RANGE;
332*ba07fd2fSBen Hoff 	vid->pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
333*ba07fd2fSBen Hoff 	vid->pix.interlaced = false;
334*ba07fd2fSBen Hoff 	vid->pix.half_size = vid->pix.sizeimage / 2;
335*ba07fd2fSBen Hoff 	hws_set_current_dv_timings(vid, vid->pix.width,
336*ba07fd2fSBen Hoff 				   vid->pix.height, vid->pix.interlaced);
337*ba07fd2fSBen Hoff 	vid->current_fps = 60;
338*ba07fd2fSBen Hoff 
339*ba07fd2fSBen Hoff 	/* color controls default (mid-scale) */
340*ba07fd2fSBen Hoff 	vid->current_brightness = 0x80;
341*ba07fd2fSBen Hoff 	vid->current_contrast = 0x80;
342*ba07fd2fSBen Hoff 	vid->current_saturation = 0x80;
343*ba07fd2fSBen Hoff 	vid->current_hue = 0x80;
344*ba07fd2fSBen Hoff 
345*ba07fd2fSBen Hoff 	/* capture state */
346*ba07fd2fSBen Hoff 	vid->cap_active = false;
347*ba07fd2fSBen Hoff 	vid->stop_requested = false;
348*ba07fd2fSBen Hoff 	vid->last_buf_half_toggle = 0;
349*ba07fd2fSBen Hoff 	vid->half_seen = false;
350*ba07fd2fSBen Hoff 	vid->signal_loss_cnt = 0;
351*ba07fd2fSBen Hoff 
352*ba07fd2fSBen Hoff 	/* Create BCHS + DV power-present as modern controls */
353*ba07fd2fSBen Hoff 	{
354*ba07fd2fSBen Hoff 		int err = hws_ctrls_init(vid);
355*ba07fd2fSBen Hoff 
356*ba07fd2fSBen Hoff 		if (err) {
357*ba07fd2fSBen Hoff 			dev_err(&pdev->pdev->dev,
358*ba07fd2fSBen Hoff 				"v4l2 ctrl init failed on ch%d: %d\n", ch, err);
359*ba07fd2fSBen Hoff 			return err;
360*ba07fd2fSBen Hoff 		}
361*ba07fd2fSBen Hoff 	}
362*ba07fd2fSBen Hoff 
363*ba07fd2fSBen Hoff 	return 0;
364*ba07fd2fSBen Hoff }
365*ba07fd2fSBen Hoff 
366*ba07fd2fSBen Hoff static void hws_video_drain_queue_locked(struct hws_video *vid)
367*ba07fd2fSBen Hoff {
368*ba07fd2fSBen Hoff 	/* Return in-flight first */
369*ba07fd2fSBen Hoff 	if (vid->active) {
370*ba07fd2fSBen Hoff 		vb2_buffer_done(&vid->active->vb.vb2_buf, VB2_BUF_STATE_ERROR);
371*ba07fd2fSBen Hoff 		vid->active = NULL;
372*ba07fd2fSBen Hoff 	}
373*ba07fd2fSBen Hoff 
374*ba07fd2fSBen Hoff 	/* Then everything queued */
375*ba07fd2fSBen Hoff 	while (!list_empty(&vid->capture_queue)) {
376*ba07fd2fSBen Hoff 		struct hwsvideo_buffer *b =
377*ba07fd2fSBen Hoff 		    list_first_entry(&vid->capture_queue,
378*ba07fd2fSBen Hoff 				     struct hwsvideo_buffer,
379*ba07fd2fSBen Hoff 				     list);
380*ba07fd2fSBen Hoff 		list_del_init(&b->list);
381*ba07fd2fSBen Hoff 		vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR);
382*ba07fd2fSBen Hoff 	}
383*ba07fd2fSBen Hoff }
384*ba07fd2fSBen Hoff 
385*ba07fd2fSBen Hoff static void hws_video_release_registration(struct hws_video *vid)
386*ba07fd2fSBen Hoff {
387*ba07fd2fSBen Hoff 	if (vid->buffer_queue.ops) {
388*ba07fd2fSBen Hoff 		vb2_queue_release(&vid->buffer_queue);
389*ba07fd2fSBen Hoff 		vid->buffer_queue.ops = NULL;
390*ba07fd2fSBen Hoff 	}
391*ba07fd2fSBen Hoff 
392*ba07fd2fSBen Hoff 	if (!vid->video_device)
393*ba07fd2fSBen Hoff 		return;
394*ba07fd2fSBen Hoff 
395*ba07fd2fSBen Hoff 	if (video_is_registered(vid->video_device))
396*ba07fd2fSBen Hoff 		vb2_video_unregister_device(vid->video_device);
397*ba07fd2fSBen Hoff 	else
398*ba07fd2fSBen Hoff 		video_device_release(vid->video_device);
399*ba07fd2fSBen Hoff 	vid->video_device = NULL;
400*ba07fd2fSBen Hoff }
401*ba07fd2fSBen Hoff 
402*ba07fd2fSBen Hoff static void hws_video_collect_done_locked(struct hws_video *vid,
403*ba07fd2fSBen Hoff 					  struct list_head *done)
404*ba07fd2fSBen Hoff {
405*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *b;
406*ba07fd2fSBen Hoff 
407*ba07fd2fSBen Hoff 	if (vid->active) {
408*ba07fd2fSBen Hoff 		if (!list_node_unlinked(&vid->active->list)) {
409*ba07fd2fSBen Hoff 			list_move_tail(&vid->active->list, done);
410*ba07fd2fSBen Hoff 		} else {
411*ba07fd2fSBen Hoff 			INIT_LIST_HEAD(&vid->active->list);
412*ba07fd2fSBen Hoff 			list_add_tail(&vid->active->list, done);
413*ba07fd2fSBen Hoff 		}
414*ba07fd2fSBen Hoff 		vid->active = NULL;
415*ba07fd2fSBen Hoff 	}
416*ba07fd2fSBen Hoff 
417*ba07fd2fSBen Hoff 	if (vid->next_prepared) {
418*ba07fd2fSBen Hoff 		if (!list_node_unlinked(&vid->next_prepared->list)) {
419*ba07fd2fSBen Hoff 			list_move_tail(&vid->next_prepared->list, done);
420*ba07fd2fSBen Hoff 		} else {
421*ba07fd2fSBen Hoff 			INIT_LIST_HEAD(&vid->next_prepared->list);
422*ba07fd2fSBen Hoff 			list_add_tail(&vid->next_prepared->list, done);
423*ba07fd2fSBen Hoff 		}
424*ba07fd2fSBen Hoff 		vid->next_prepared = NULL;
425*ba07fd2fSBen Hoff 	}
426*ba07fd2fSBen Hoff 
427*ba07fd2fSBen Hoff 	while (!list_empty(&vid->capture_queue)) {
428*ba07fd2fSBen Hoff 		b = list_first_entry(&vid->capture_queue, struct hwsvideo_buffer,
429*ba07fd2fSBen Hoff 				     list);
430*ba07fd2fSBen Hoff 		list_move_tail(&b->list, done);
431*ba07fd2fSBen Hoff 	}
432*ba07fd2fSBen Hoff 
433*ba07fd2fSBen Hoff 	vid->queued_count = 0;
434*ba07fd2fSBen Hoff }
435*ba07fd2fSBen Hoff 
436*ba07fd2fSBen Hoff void hws_video_cleanup_channel(struct hws_pcie_dev *pdev, int ch)
437*ba07fd2fSBen Hoff {
438*ba07fd2fSBen Hoff 	struct hws_video *vid;
439*ba07fd2fSBen Hoff 	unsigned long flags;
440*ba07fd2fSBen Hoff 
441*ba07fd2fSBen Hoff 	if (!pdev || ch < 0 || ch >= pdev->max_channels)
442*ba07fd2fSBen Hoff 		return;
443*ba07fd2fSBen Hoff 
444*ba07fd2fSBen Hoff 	vid = &pdev->video[ch];
445*ba07fd2fSBen Hoff 
446*ba07fd2fSBen Hoff 	/* 1) Stop HW best-effort for this channel */
447*ba07fd2fSBen Hoff 	hws_enable_video_capture(vid->parent, vid->channel_index, false);
448*ba07fd2fSBen Hoff 
449*ba07fd2fSBen Hoff 	/* 2) Flip software state so IRQ/BH will be no-ops if they run */
450*ba07fd2fSBen Hoff 	WRITE_ONCE(vid->stop_requested, true);
451*ba07fd2fSBen Hoff 	WRITE_ONCE(vid->cap_active, false);
452*ba07fd2fSBen Hoff 
453*ba07fd2fSBen Hoff 	/* 3) Ensure the IRQ handler finished any in-flight completions */
454*ba07fd2fSBen Hoff 	if (vid->parent && vid->parent->irq >= 0)
455*ba07fd2fSBen Hoff 		synchronize_irq(vid->parent->irq);
456*ba07fd2fSBen Hoff 
457*ba07fd2fSBen Hoff 	/* 4) Drain SW capture queue & in-flight under lock */
458*ba07fd2fSBen Hoff 	spin_lock_irqsave(&vid->irq_lock, flags);
459*ba07fd2fSBen Hoff 	hws_video_drain_queue_locked(vid);
460*ba07fd2fSBen Hoff 	spin_unlock_irqrestore(&vid->irq_lock, flags);
461*ba07fd2fSBen Hoff 
462*ba07fd2fSBen Hoff 	/* 5) Release VB2 queue if initialized */
463*ba07fd2fSBen Hoff 	hws_video_release_registration(vid);
464*ba07fd2fSBen Hoff 
465*ba07fd2fSBen Hoff 	/* 6) Free V4L2 controls */
466*ba07fd2fSBen Hoff 	v4l2_ctrl_handler_free(&vid->control_handler);
467*ba07fd2fSBen Hoff 
468*ba07fd2fSBen Hoff 	/* 8) Reset simple state; do not memset the whole struct here. */
469*ba07fd2fSBen Hoff 	mutex_destroy(&vid->state_lock);
470*ba07fd2fSBen Hoff 	INIT_LIST_HEAD(&vid->capture_queue);
471*ba07fd2fSBen Hoff 	vid->active = NULL;
472*ba07fd2fSBen Hoff 	vid->stop_requested = false;
473*ba07fd2fSBen Hoff 	vid->last_buf_half_toggle = 0;
474*ba07fd2fSBen Hoff 	vid->half_seen = false;
475*ba07fd2fSBen Hoff 	vid->signal_loss_cnt = 0;
476*ba07fd2fSBen Hoff }
477*ba07fd2fSBen Hoff 
478*ba07fd2fSBen Hoff /* Convenience cast */
479*ba07fd2fSBen Hoff static inline struct hwsvideo_buffer *to_hwsbuf(struct vb2_buffer *vb)
480*ba07fd2fSBen Hoff {
481*ba07fd2fSBen Hoff 	return container_of(to_vb2_v4l2_buffer(vb), struct hwsvideo_buffer, vb);
482*ba07fd2fSBen Hoff }
483*ba07fd2fSBen Hoff 
484*ba07fd2fSBen Hoff static int hws_buf_init(struct vb2_buffer *vb)
485*ba07fd2fSBen Hoff {
486*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *b = to_hwsbuf(vb);
487*ba07fd2fSBen Hoff 
488*ba07fd2fSBen Hoff 	INIT_LIST_HEAD(&b->list);
489*ba07fd2fSBen Hoff 	return 0;
490*ba07fd2fSBen Hoff }
491*ba07fd2fSBen Hoff 
492*ba07fd2fSBen Hoff static void hws_buf_finish(struct vb2_buffer *vb)
493*ba07fd2fSBen Hoff {
494*ba07fd2fSBen Hoff 	/* vb2 core handles cache maintenance for dma-contig buffers */
495*ba07fd2fSBen Hoff 	(void)vb;
496*ba07fd2fSBen Hoff }
497*ba07fd2fSBen Hoff 
498*ba07fd2fSBen Hoff static void hws_buf_cleanup(struct vb2_buffer *vb)
499*ba07fd2fSBen Hoff {
500*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *b = to_hwsbuf(vb);
501*ba07fd2fSBen Hoff 
502*ba07fd2fSBen Hoff 	if (!list_empty(&b->list))
503*ba07fd2fSBen Hoff 		list_del_init(&b->list);
504*ba07fd2fSBen Hoff }
505*ba07fd2fSBen Hoff 
506*ba07fd2fSBen Hoff void hws_program_dma_for_addr(struct hws_pcie_dev *hws, unsigned int ch,
507*ba07fd2fSBen Hoff 			      dma_addr_t dma)
508*ba07fd2fSBen Hoff {
509*ba07fd2fSBen Hoff 	struct hws_video *vid = &hws->video[ch];
510*ba07fd2fSBen Hoff 
511*ba07fd2fSBen Hoff 	hws_program_dma_window(vid, dma);
512*ba07fd2fSBen Hoff }
513*ba07fd2fSBen Hoff 
514*ba07fd2fSBen Hoff void hws_enable_video_capture(struct hws_pcie_dev *hws, unsigned int chan,
515*ba07fd2fSBen Hoff 			      bool on)
516*ba07fd2fSBen Hoff {
517*ba07fd2fSBen Hoff 	u32 status;
518*ba07fd2fSBen Hoff 
519*ba07fd2fSBen Hoff 	if (!hws || hws->pci_lost || chan >= hws->max_channels)
520*ba07fd2fSBen Hoff 		return;
521*ba07fd2fSBen Hoff 
522*ba07fd2fSBen Hoff 	status = readl(hws->bar0_base + HWS_REG_VCAP_ENABLE);
523*ba07fd2fSBen Hoff 	status = on ? (status | BIT(chan)) : (status & ~BIT(chan));
524*ba07fd2fSBen Hoff 	writel(status, hws->bar0_base + HWS_REG_VCAP_ENABLE);
525*ba07fd2fSBen Hoff 	(void)readl(hws->bar0_base + HWS_REG_VCAP_ENABLE);
526*ba07fd2fSBen Hoff 
527*ba07fd2fSBen Hoff 	WRITE_ONCE(hws->video[chan].cap_active, on);
528*ba07fd2fSBen Hoff 
529*ba07fd2fSBen Hoff 	dev_dbg(&hws->pdev->dev, "vcap %s ch%u (reg=0x%08x)\n",
530*ba07fd2fSBen Hoff 		on ? "ON" : "OFF", chan, status);
531*ba07fd2fSBen Hoff }
532*ba07fd2fSBen Hoff 
533*ba07fd2fSBen Hoff static void hws_seed_dma_windows(struct hws_pcie_dev *hws)
534*ba07fd2fSBen Hoff {
535*ba07fd2fSBen Hoff 	const u32 addr_mask = PCI_E_BAR_ADD_MASK;
536*ba07fd2fSBen Hoff 	const u32 addr_low_mask = PCI_E_BAR_ADD_LOWMASK;
537*ba07fd2fSBen Hoff 	u32 table = 0x208;	/* one 64-bit entry per channel */
538*ba07fd2fSBen Hoff 	unsigned int ch;
539*ba07fd2fSBen Hoff 
540*ba07fd2fSBen Hoff 	if (!hws || !hws->bar0_base)
541*ba07fd2fSBen Hoff 		return;
542*ba07fd2fSBen Hoff 
543*ba07fd2fSBen Hoff 	/* If cur_max_video_ch is not set yet, default to max_channels. */
544*ba07fd2fSBen Hoff 	if (!hws->cur_max_video_ch || hws->cur_max_video_ch > hws->max_channels)
545*ba07fd2fSBen Hoff 		hws->cur_max_video_ch = hws->max_channels;
546*ba07fd2fSBen Hoff 
547*ba07fd2fSBen Hoff 	for (ch = 0; ch < hws->cur_max_video_ch; ch++, table += 8) {
548*ba07fd2fSBen Hoff 		if (!hws->scratch_vid[ch].cpu)
549*ba07fd2fSBen Hoff 			continue;
550*ba07fd2fSBen Hoff 
551*ba07fd2fSBen Hoff 		/* Program 64-bit BAR remap entry for this channel */
552*ba07fd2fSBen Hoff 		{
553*ba07fd2fSBen Hoff 			dma_addr_t p = hws->scratch_vid[ch].dma;
554*ba07fd2fSBen Hoff 			u32 lo = lower_32_bits(p) & addr_mask;
555*ba07fd2fSBen Hoff 			u32 hi = upper_32_bits(p);
556*ba07fd2fSBen Hoff 			u32 pci_addr_low = lower_32_bits(p) & addr_low_mask;
557*ba07fd2fSBen Hoff 
558*ba07fd2fSBen Hoff 			writel_relaxed(hi,
559*ba07fd2fSBen Hoff 				       hws->bar0_base + PCI_ADDR_TABLE_BASE +
560*ba07fd2fSBen Hoff 				       table);
561*ba07fd2fSBen Hoff 			writel_relaxed(lo,
562*ba07fd2fSBen Hoff 				       hws->bar0_base + PCI_ADDR_TABLE_BASE +
563*ba07fd2fSBen Hoff 				       table + PCIE_BARADDROFSIZE);
564*ba07fd2fSBen Hoff 
565*ba07fd2fSBen Hoff 			/* Per-channel AXI base + PCI low */
566*ba07fd2fSBen Hoff 			writel_relaxed((ch + 1) * PCIEBAR_AXI_BASE +
567*ba07fd2fSBen Hoff 				       pci_addr_low,
568*ba07fd2fSBen Hoff 				       hws->bar0_base + CVBS_IN_BUF_BASE +
569*ba07fd2fSBen Hoff 				       ch * PCIE_BARADDROFSIZE);
570*ba07fd2fSBen Hoff 
571*ba07fd2fSBen Hoff 			/* Half-frame length in /16 units.
572*ba07fd2fSBen Hoff 			 * Prefer the current channel's computed half_size if available.
573*ba07fd2fSBen Hoff 			 * Fall back to half of the probe-owned scratch buffer.
574*ba07fd2fSBen Hoff 			 */
575*ba07fd2fSBen Hoff 			{
576*ba07fd2fSBen Hoff 				u32 half_bytes = hws->video[ch].pix.half_size ?
577*ba07fd2fSBen Hoff 				    hws->video[ch].pix.half_size :
578*ba07fd2fSBen Hoff 				    (u32)(hws->scratch_vid[ch].size / 2);
579*ba07fd2fSBen Hoff 				writel_relaxed(half_bytes / 16,
580*ba07fd2fSBen Hoff 					       hws->bar0_base +
581*ba07fd2fSBen Hoff 					       CVBS_IN_BUF_BASE2 +
582*ba07fd2fSBen Hoff 					       ch * PCIE_BARADDROFSIZE);
583*ba07fd2fSBen Hoff 			}
584*ba07fd2fSBen Hoff 		}
585*ba07fd2fSBen Hoff 	}
586*ba07fd2fSBen Hoff 
587*ba07fd2fSBen Hoff 	/* Post writes so device sees them before we move on */
588*ba07fd2fSBen Hoff 	(void)readl(hws->bar0_base + HWS_REG_INT_STATUS);
589*ba07fd2fSBen Hoff }
590*ba07fd2fSBen Hoff 
591*ba07fd2fSBen Hoff static void hws_ack_all_irqs(struct hws_pcie_dev *hws)
592*ba07fd2fSBen Hoff {
593*ba07fd2fSBen Hoff 	u32 st = readl(hws->bar0_base + HWS_REG_INT_STATUS);
594*ba07fd2fSBen Hoff 
595*ba07fd2fSBen Hoff 	if (st) {
596*ba07fd2fSBen Hoff 		writel(st, hws->bar0_base + HWS_REG_INT_STATUS);	/* W1C */
597*ba07fd2fSBen Hoff 		(void)readl(hws->bar0_base + HWS_REG_INT_STATUS);
598*ba07fd2fSBen Hoff 	}
599*ba07fd2fSBen Hoff }
600*ba07fd2fSBen Hoff 
601*ba07fd2fSBen Hoff static void hws_open_irq_fabric(struct hws_pcie_dev *hws)
602*ba07fd2fSBen Hoff {
603*ba07fd2fSBen Hoff 	/* Route all sources to vector 0. */
604*ba07fd2fSBen Hoff 	writel(0x00000000, hws->bar0_base + PCIE_INT_DEC_REG_BASE);
605*ba07fd2fSBen Hoff 	(void)readl(hws->bar0_base + PCIE_INT_DEC_REG_BASE);
606*ba07fd2fSBen Hoff 
607*ba07fd2fSBen Hoff 	/* Enable the PCIe bridge. */
608*ba07fd2fSBen Hoff 	writel(0x00000001, hws->bar0_base + PCIEBR_EN_REG_BASE);
609*ba07fd2fSBen Hoff 	(void)readl(hws->bar0_base + PCIEBR_EN_REG_BASE);
610*ba07fd2fSBen Hoff 
611*ba07fd2fSBen Hoff 	/* Open the global/bridge gate (legacy 0x3FFFF) */
612*ba07fd2fSBen Hoff 	writel(HWS_INT_EN_MASK, hws->bar0_base + INT_EN_REG_BASE);
613*ba07fd2fSBen Hoff 	(void)readl(hws->bar0_base + INT_EN_REG_BASE);
614*ba07fd2fSBen Hoff }
615*ba07fd2fSBen Hoff 
616*ba07fd2fSBen Hoff void hws_init_video_sys(struct hws_pcie_dev *hws, bool enable)
617*ba07fd2fSBen Hoff {
618*ba07fd2fSBen Hoff 	int i;
619*ba07fd2fSBen Hoff 
620*ba07fd2fSBen Hoff 	if (hws->start_run && !enable)
621*ba07fd2fSBen Hoff 		return;
622*ba07fd2fSBen Hoff 
623*ba07fd2fSBen Hoff 	/* 1) reset the decoder mode register to 0 */
624*ba07fd2fSBen Hoff 	writel(0x00000000, hws->bar0_base + HWS_REG_DEC_MODE);
625*ba07fd2fSBen Hoff 	hws_seed_dma_windows(hws);
626*ba07fd2fSBen Hoff 
627*ba07fd2fSBen Hoff 	/* 3) on a full reset, clear all per-channel status and indices */
628*ba07fd2fSBen Hoff 	if (!enable) {
629*ba07fd2fSBen Hoff 		for (i = 0; i < hws->max_channels; i++) {
630*ba07fd2fSBen Hoff 			/* helpers to arm/disable capture engines */
631*ba07fd2fSBen Hoff 			hws_enable_video_capture(hws, i, false);
632*ba07fd2fSBen Hoff 		}
633*ba07fd2fSBen Hoff 	}
634*ba07fd2fSBen Hoff 
635*ba07fd2fSBen Hoff 	/* 4) Start run: set bit31, wait a bit, then program low 24 bits. */
636*ba07fd2fSBen Hoff 	writel(0x80000000, hws->bar0_base + HWS_REG_DEC_MODE);
637*ba07fd2fSBen Hoff 	writel(0x80FFFFFF, hws->bar0_base + HWS_REG_DEC_MODE);
638*ba07fd2fSBen Hoff 	writel(0x13, hws->bar0_base + HWS_REG_DEC_MODE);
639*ba07fd2fSBen Hoff 	hws_ack_all_irqs(hws);
640*ba07fd2fSBen Hoff 	hws_open_irq_fabric(hws);
641*ba07fd2fSBen Hoff 	/* 6) record that we're now running */
642*ba07fd2fSBen Hoff 	hws->start_run = true;
643*ba07fd2fSBen Hoff }
644*ba07fd2fSBen Hoff 
645*ba07fd2fSBen Hoff int hws_check_card_status(struct hws_pcie_dev *hws)
646*ba07fd2fSBen Hoff {
647*ba07fd2fSBen Hoff 	u32 status;
648*ba07fd2fSBen Hoff 
649*ba07fd2fSBen Hoff 	if (!hws || !hws->bar0_base)
650*ba07fd2fSBen Hoff 		return -ENODEV;
651*ba07fd2fSBen Hoff 
652*ba07fd2fSBen Hoff 	status = readl(hws->bar0_base + HWS_REG_SYS_STATUS);
653*ba07fd2fSBen Hoff 
654*ba07fd2fSBen Hoff 	/* Common device-missing pattern. */
655*ba07fd2fSBen Hoff 	if (status == 0xFFFFFFFF) {
656*ba07fd2fSBen Hoff 		hws->pci_lost = true;
657*ba07fd2fSBen Hoff 		dev_err(&hws->pdev->dev, "PCIe device not responding\n");
658*ba07fd2fSBen Hoff 		return -ENODEV;
659*ba07fd2fSBen Hoff 	}
660*ba07fd2fSBen Hoff 
661*ba07fd2fSBen Hoff 	/* If RUN/READY bit (bit0) is not set, reinitialize the video core. */
662*ba07fd2fSBen Hoff 	if (!(status & BIT(0))) {
663*ba07fd2fSBen Hoff 		dev_dbg(&hws->pdev->dev,
664*ba07fd2fSBen Hoff 			"SYS_STATUS not ready (0x%08x), reinitializing\n",
665*ba07fd2fSBen Hoff 			status);
666*ba07fd2fSBen Hoff 		hws_init_video_sys(hws, true);
667*ba07fd2fSBen Hoff 	}
668*ba07fd2fSBen Hoff 
669*ba07fd2fSBen Hoff 	return 0;
670*ba07fd2fSBen Hoff }
671*ba07fd2fSBen Hoff 
672*ba07fd2fSBen Hoff void check_video_format(struct hws_pcie_dev *pdx)
673*ba07fd2fSBen Hoff {
674*ba07fd2fSBen Hoff 	int i;
675*ba07fd2fSBen Hoff 
676*ba07fd2fSBen Hoff 	for (i = 0; i < pdx->cur_max_video_ch; i++) {
677*ba07fd2fSBen Hoff 		bool interlace = false;
678*ba07fd2fSBen Hoff 
679*ba07fd2fSBen Hoff 		if (!hws_read_active_state(pdx, i, &interlace)) {
680*ba07fd2fSBen Hoff 			/* No active video; optionally feed neutral frames to keep streaming. */
681*ba07fd2fSBen Hoff 			if (pdx->video[i].signal_loss_cnt == 0)
682*ba07fd2fSBen Hoff 				pdx->video[i].signal_loss_cnt = 1;
683*ba07fd2fSBen Hoff 			if (READ_ONCE(pdx->video[i].cap_active))
684*ba07fd2fSBen Hoff 				hws_force_no_signal_frame(&pdx->video[i],
685*ba07fd2fSBen Hoff 							  "monitor_nosignal");
686*ba07fd2fSBen Hoff 		} else {
687*ba07fd2fSBen Hoff 			if (pdx->hw_ver > 0)
688*ba07fd2fSBen Hoff 				handle_hwv2_path(pdx, i);
689*ba07fd2fSBen Hoff 			else
690*ba07fd2fSBen Hoff 				/* Legacy path stub; see handle_legacy_path() comment. */
691*ba07fd2fSBen Hoff 				handle_legacy_path(pdx, i);
692*ba07fd2fSBen Hoff 
693*ba07fd2fSBen Hoff 			update_live_resolution(pdx, i, interlace);
694*ba07fd2fSBen Hoff 			pdx->video[i].signal_loss_cnt = 0;
695*ba07fd2fSBen Hoff 		}
696*ba07fd2fSBen Hoff 	}
697*ba07fd2fSBen Hoff }
698*ba07fd2fSBen Hoff 
699*ba07fd2fSBen Hoff static inline void hws_write_if_diff(struct hws_pcie_dev *hws, u32 reg_off,
700*ba07fd2fSBen Hoff 				     u32 new_val)
701*ba07fd2fSBen Hoff {
702*ba07fd2fSBen Hoff 	void __iomem *addr;
703*ba07fd2fSBen Hoff 	u32 old;
704*ba07fd2fSBen Hoff 
705*ba07fd2fSBen Hoff 	if (!hws || !hws->bar0_base)
706*ba07fd2fSBen Hoff 		return;
707*ba07fd2fSBen Hoff 
708*ba07fd2fSBen Hoff 	addr = hws->bar0_base + reg_off;
709*ba07fd2fSBen Hoff 
710*ba07fd2fSBen Hoff 	old = readl(addr);
711*ba07fd2fSBen Hoff 	/* Treat all-ones as device gone; avoid writing garbage. */
712*ba07fd2fSBen Hoff 	if (old == 0xFFFFFFFF) {
713*ba07fd2fSBen Hoff 		hws->pci_lost = true;
714*ba07fd2fSBen Hoff 		return;
715*ba07fd2fSBen Hoff 	}
716*ba07fd2fSBen Hoff 
717*ba07fd2fSBen Hoff 	if (old != new_val) {
718*ba07fd2fSBen Hoff 		writel(new_val, addr);
719*ba07fd2fSBen Hoff 		/* Post the write on some bridges / enforce ordering. */
720*ba07fd2fSBen Hoff 		(void)readl(addr);
721*ba07fd2fSBen Hoff 	}
722*ba07fd2fSBen Hoff }
723*ba07fd2fSBen Hoff 
724*ba07fd2fSBen Hoff static bool hws_read_active_state(struct hws_pcie_dev *pdx, unsigned int ch,
725*ba07fd2fSBen Hoff 				  bool *interlace)
726*ba07fd2fSBen Hoff {
727*ba07fd2fSBen Hoff 	u32 reg;
728*ba07fd2fSBen Hoff 	bool active;
729*ba07fd2fSBen Hoff 
730*ba07fd2fSBen Hoff 	if (ch >= pdx->cur_max_video_ch)
731*ba07fd2fSBen Hoff 		return false;
732*ba07fd2fSBen Hoff 
733*ba07fd2fSBen Hoff 	reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS);
734*ba07fd2fSBen Hoff 	active = !!(reg & BIT(ch));
735*ba07fd2fSBen Hoff 	if (interlace)
736*ba07fd2fSBen Hoff 		*interlace = !!(reg & BIT(8 + ch));
737*ba07fd2fSBen Hoff 	return active;
738*ba07fd2fSBen Hoff }
739*ba07fd2fSBen Hoff 
740*ba07fd2fSBen Hoff /* Modern hardware path: keep HW registers in sync with current per-channel
741*ba07fd2fSBen Hoff  * software state.
742*ba07fd2fSBen Hoff  */
743*ba07fd2fSBen Hoff static void handle_hwv2_path(struct hws_pcie_dev *hws, unsigned int ch)
744*ba07fd2fSBen Hoff {
745*ba07fd2fSBen Hoff 	struct hws_video *vid;
746*ba07fd2fSBen Hoff 	u32 reg, in_fps, cur_out_res, want_out_res;
747*ba07fd2fSBen Hoff 
748*ba07fd2fSBen Hoff 	if (!hws || !hws->bar0_base || ch >= hws->max_channels)
749*ba07fd2fSBen Hoff 		return;
750*ba07fd2fSBen Hoff 
751*ba07fd2fSBen Hoff 	vid = &hws->video[ch];
752*ba07fd2fSBen Hoff 
753*ba07fd2fSBen Hoff 	/* 1) Input frame rate (read-only; log or export via debugfs if wanted) */
754*ba07fd2fSBen Hoff 	in_fps = readl(hws->bar0_base + HWS_REG_FRAME_RATE(ch));
755*ba07fd2fSBen Hoff 	/* dev_dbg(&hws->pdev->dev, "ch%u input fps=%u\n", ch, in_fps); */
756*ba07fd2fSBen Hoff 	(void)in_fps;
757*ba07fd2fSBen Hoff 
758*ba07fd2fSBen Hoff 	/* 2) Output resolution programming.
759*ba07fd2fSBen Hoff 	 * For now, mirror the current format to OUT_RES.
760*ba07fd2fSBen Hoff 	 */
761*ba07fd2fSBen Hoff 	want_out_res = (vid->pix.height << 16) | vid->pix.width;
762*ba07fd2fSBen Hoff 	cur_out_res = readl(hws->bar0_base + HWS_REG_OUT_RES(ch));
763*ba07fd2fSBen Hoff 	if (cur_out_res != want_out_res)
764*ba07fd2fSBen Hoff 		hws_write_if_diff(hws, HWS_REG_OUT_RES(ch), want_out_res);
765*ba07fd2fSBen Hoff 
766*ba07fd2fSBen Hoff 	/* 3) Output FPS: only program if you actually track a target.
767*ba07fd2fSBen Hoff 	 * Example heuristic (disabled by default):
768*ba07fd2fSBen Hoff 	 *
769*ba07fd2fSBen Hoff 	 *   u32 out_fps = (vid->fmt_curr.height >= 1080) ? 60 : 30;
770*ba07fd2fSBen Hoff 	 *   hws_write_if_diff(hws, HWS_REG_OUT_FRAME_RATE(ch), out_fps);
771*ba07fd2fSBen Hoff 	 */
772*ba07fd2fSBen Hoff 
773*ba07fd2fSBen Hoff 	/* 4) BCHS controls: pack from per-channel current_* fields */
774*ba07fd2fSBen Hoff 	reg = readl(hws->bar0_base + HWS_REG_BCHS(ch));
775*ba07fd2fSBen Hoff 	{
776*ba07fd2fSBen Hoff 		u8 br = reg & 0xFF;
777*ba07fd2fSBen Hoff 		u8 co = (reg >> 8) & 0xFF;
778*ba07fd2fSBen Hoff 		u8 hu = (reg >> 16) & 0xFF;
779*ba07fd2fSBen Hoff 		u8 sa = (reg >> 24) & 0xFF;
780*ba07fd2fSBen Hoff 
781*ba07fd2fSBen Hoff 		if (br != vid->current_brightness ||
782*ba07fd2fSBen Hoff 		    co != vid->current_contrast || hu != vid->current_hue ||
783*ba07fd2fSBen Hoff 		    sa != vid->current_saturation) {
784*ba07fd2fSBen Hoff 			u32 packed = (vid->current_saturation << 24) |
785*ba07fd2fSBen Hoff 			    (vid->current_hue << 16) |
786*ba07fd2fSBen Hoff 			    (vid->current_contrast << 8) |
787*ba07fd2fSBen Hoff 			    vid->current_brightness;
788*ba07fd2fSBen Hoff 			hws_write_if_diff(hws, HWS_REG_BCHS(ch), packed);
789*ba07fd2fSBen Hoff 		}
790*ba07fd2fSBen Hoff 	}
791*ba07fd2fSBen Hoff 
792*ba07fd2fSBen Hoff 	/* 5) HDCP detect: read only (no cache field in your structs today) */
793*ba07fd2fSBen Hoff 	reg = readl(hws->bar0_base + HWS_REG_HDCP_STATUS);
794*ba07fd2fSBen Hoff 	/* bool hdcp = !!(reg & BIT(ch)); // use if you later add a field/control */
795*ba07fd2fSBen Hoff }
796*ba07fd2fSBen Hoff 
797*ba07fd2fSBen Hoff static void handle_legacy_path(struct hws_pcie_dev *hws, unsigned int ch)
798*ba07fd2fSBen Hoff {
799*ba07fd2fSBen Hoff 	/*
800*ba07fd2fSBen Hoff 	 * Legacy (hw_ver == 0) expected behavior:
801*ba07fd2fSBen Hoff 	 * - A per-channel SW FPS accumulator incremented on each VDONE.
802*ba07fd2fSBen Hoff 	 * - A once-per-second poll mapped the count to discrete FPS:
803*ba07fd2fSBen Hoff 	 *   >55*2 => 60, >45*2 => 50, >25*2 => 30, >20*2 => 25, else 60,
804*ba07fd2fSBen Hoff 	 *   then reset the accumulator to 0.
805*ba07fd2fSBen Hoff 	 * - The *2 factor assumed VDONE fired per-field; if legacy VDONE is
806*ba07fd2fSBen Hoff 	 *   per-frame, drop the factor.
807*ba07fd2fSBen Hoff 	 *
808*ba07fd2fSBen Hoff 	 * Current code keeps this path as a no-op; vid->current_fps stays at the
809*ba07fd2fSBen Hoff 	 * default or mode-derived value. If accurate legacy FPS reporting is
810*ba07fd2fSBen Hoff 	 * needed (V4L2 g_parm/timeperframe), reintroduce the accumulator in the
811*ba07fd2fSBen Hoff 	 * IRQ path and perform the mapping/reset here.
812*ba07fd2fSBen Hoff 	 *
813*ba07fd2fSBen Hoff 	 * No-op by default. If you introduce a SW FPS accumulator, map it here.
814*ba07fd2fSBen Hoff 	 *
815*ba07fd2fSBen Hoff 	 * Example skeleton:
816*ba07fd2fSBen Hoff 	 *
817*ba07fd2fSBen Hoff 	 *   u32 sw_rate = READ_ONCE(hws->sw_fps[ch]); // incremented elsewhere
818*ba07fd2fSBen Hoff 	 *   if (sw_rate > THRESHOLD) {
819*ba07fd2fSBen Hoff 	 *       u32 fps = pick_fps_from_rate(sw_rate);
820*ba07fd2fSBen Hoff 	 *       hws_write_if_diff(hws, HWS_REG_OUT_FRAME_RATE(ch), fps);
821*ba07fd2fSBen Hoff 	 *       WRITE_ONCE(hws->sw_fps[ch], 0);
822*ba07fd2fSBen Hoff 	 *   }
823*ba07fd2fSBen Hoff 	 */
824*ba07fd2fSBen Hoff 	(void)hws;
825*ba07fd2fSBen Hoff 	(void)ch;
826*ba07fd2fSBen Hoff }
827*ba07fd2fSBen Hoff 
828*ba07fd2fSBen Hoff static void hws_video_apply_mode_change(struct hws_pcie_dev *pdx,
829*ba07fd2fSBen Hoff 					unsigned int ch, u16 w, u16 h,
830*ba07fd2fSBen Hoff 					bool interlaced, u32 fps)
831*ba07fd2fSBen Hoff {
832*ba07fd2fSBen Hoff 	struct hws_video *v = &pdx->video[ch];
833*ba07fd2fSBen Hoff 	unsigned long flags;
834*ba07fd2fSBen Hoff 	bool queue_busy;
835*ba07fd2fSBen Hoff 	bool geometry_changed;
836*ba07fd2fSBen Hoff 	struct list_head done;
837*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *b, *tmp;
838*ba07fd2fSBen Hoff 
839*ba07fd2fSBen Hoff 	if (!pdx || !pdx->bar0_base)
840*ba07fd2fSBen Hoff 		return;
841*ba07fd2fSBen Hoff 	if (ch >= pdx->max_channels)
842*ba07fd2fSBen Hoff 		return;
843*ba07fd2fSBen Hoff 	if (!w || !h || w > MAX_VIDEO_HW_W ||
844*ba07fd2fSBen Hoff 	    (!interlaced && h > MAX_VIDEO_HW_H) ||
845*ba07fd2fSBen Hoff 	    (interlaced && (h * 2) > MAX_VIDEO_HW_H))
846*ba07fd2fSBen Hoff 		return;
847*ba07fd2fSBen Hoff 	if (!fps || fps == 0xFFFFFFFF || fps > 240)
848*ba07fd2fSBen Hoff 		fps = (h == 576) ? 50 : 60;
849*ba07fd2fSBen Hoff 
850*ba07fd2fSBen Hoff 	geometry_changed = w != v->pix.width || h != v->pix.height ||
851*ba07fd2fSBen Hoff 		interlaced != v->pix.interlaced;
852*ba07fd2fSBen Hoff 	if (!geometry_changed && fps == v->current_fps)
853*ba07fd2fSBen Hoff 		return;
854*ba07fd2fSBen Hoff 
855*ba07fd2fSBen Hoff 	if (!geometry_changed) {
856*ba07fd2fSBen Hoff 		/* Refresh cached live timing state, but don't emit a resolution
857*ba07fd2fSBen Hoff 		 * change event when only the frame rate changes.
858*ba07fd2fSBen Hoff 		 */
859*ba07fd2fSBen Hoff 		mutex_lock(&v->state_lock);
860*ba07fd2fSBen Hoff 		v->pix.interlaced = interlaced;
861*ba07fd2fSBen Hoff 		v->pix.field = interlaced ? V4L2_FIELD_INTERLACED :
862*ba07fd2fSBen Hoff 					    V4L2_FIELD_NONE;
863*ba07fd2fSBen Hoff 		hws_set_current_dv_timings(v, w, h, interlaced);
864*ba07fd2fSBen Hoff 		v->current_fps = fps;
865*ba07fd2fSBen Hoff 		mutex_unlock(&v->state_lock);
866*ba07fd2fSBen Hoff 		return;
867*ba07fd2fSBen Hoff 	}
868*ba07fd2fSBen Hoff 
869*ba07fd2fSBen Hoff 	if (!mutex_trylock(&v->state_lock))
870*ba07fd2fSBen Hoff 		return;
871*ba07fd2fSBen Hoff 
872*ba07fd2fSBen Hoff 	INIT_LIST_HEAD(&done);
873*ba07fd2fSBen Hoff 	queue_busy = vb2_is_busy(&v->buffer_queue);
874*ba07fd2fSBen Hoff 
875*ba07fd2fSBen Hoff 	WRITE_ONCE(v->stop_requested, true);
876*ba07fd2fSBen Hoff 	WRITE_ONCE(v->cap_active, false);
877*ba07fd2fSBen Hoff 	/* Publish software stop first so the IRQ completion path sees the stop
878*ba07fd2fSBen Hoff 	 * before we touch MMIO or the lists. Pairs with READ_ONCE() checks in the
879*ba07fd2fSBen Hoff 	 * VDONE handler and hws_arm_next() to prevent completions while modes
880*ba07fd2fSBen Hoff 	 * change.
881*ba07fd2fSBen Hoff 	 */
882*ba07fd2fSBen Hoff 	smp_wmb();
883*ba07fd2fSBen Hoff 
884*ba07fd2fSBen Hoff 	hws_enable_video_capture(pdx, ch, false);
885*ba07fd2fSBen Hoff 	readl(pdx->bar0_base + HWS_REG_INT_STATUS);
886*ba07fd2fSBen Hoff 
887*ba07fd2fSBen Hoff 	if (v->parent && v->parent->irq >= 0)
888*ba07fd2fSBen Hoff 		synchronize_irq(v->parent->irq);
889*ba07fd2fSBen Hoff 
890*ba07fd2fSBen Hoff 	spin_lock_irqsave(&v->irq_lock, flags);
891*ba07fd2fSBen Hoff 	hws_video_collect_done_locked(v, &done);
892*ba07fd2fSBen Hoff 	spin_unlock_irqrestore(&v->irq_lock, flags);
893*ba07fd2fSBen Hoff 
894*ba07fd2fSBen Hoff 	/* Update software pixel state */
895*ba07fd2fSBen Hoff 	v->pix.width = w;
896*ba07fd2fSBen Hoff 	v->pix.height = h;
897*ba07fd2fSBen Hoff 	v->pix.interlaced = interlaced;
898*ba07fd2fSBen Hoff 	hws_set_current_dv_timings(v, w, h, interlaced);
899*ba07fd2fSBen Hoff 	v->current_fps = fps;
900*ba07fd2fSBen Hoff 
901*ba07fd2fSBen Hoff 	hws_calc_sizeimage(v, w, h, interlaced);
902*ba07fd2fSBen Hoff 	v->window_valid = false;
903*ba07fd2fSBen Hoff 
904*ba07fd2fSBen Hoff 	/* Geometry changes require userspace renegotiation once buffers exist.
905*ba07fd2fSBen Hoff 	 * Emit SOURCE_CHANGE, mark the queue in error, and let userspace
906*ba07fd2fSBen Hoff 	 * STREAMOFF/REQBUFS/STREAMON rather than trying to restart capture
907*ba07fd2fSBen Hoff 	 * with partially drained in-flight state.
908*ba07fd2fSBen Hoff 	 */
909*ba07fd2fSBen Hoff 	if (queue_busy) {
910*ba07fd2fSBen Hoff 		struct v4l2_event ev = {
911*ba07fd2fSBen Hoff 			.type = V4L2_EVENT_SOURCE_CHANGE,
912*ba07fd2fSBen Hoff 		};
913*ba07fd2fSBen Hoff 
914*ba07fd2fSBen Hoff 		ev.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION;
915*ba07fd2fSBen Hoff 		v4l2_event_queue(v->video_device, &ev);
916*ba07fd2fSBen Hoff 		vb2_queue_error(&v->buffer_queue);
917*ba07fd2fSBen Hoff 	} else {
918*ba07fd2fSBen Hoff 		WRITE_ONCE(v->stop_requested, false);
919*ba07fd2fSBen Hoff 	}
920*ba07fd2fSBen Hoff 
921*ba07fd2fSBen Hoff 	/* Program HW with new resolution */
922*ba07fd2fSBen Hoff 	hws_write_if_diff(pdx, HWS_REG_OUT_RES(ch), (h << 16) | w);
923*ba07fd2fSBen Hoff 
924*ba07fd2fSBen Hoff 	/* Legacy half-buffer programming */
925*ba07fd2fSBen Hoff 	writel(v->pix.half_size / 16,
926*ba07fd2fSBen Hoff 	       pdx->bar0_base + CVBS_IN_BUF_BASE2 + ch * PCIE_BARADDROFSIZE);
927*ba07fd2fSBen Hoff 	(void)readl(pdx->bar0_base + CVBS_IN_BUF_BASE2 +
928*ba07fd2fSBen Hoff 		    ch * PCIE_BARADDROFSIZE);
929*ba07fd2fSBen Hoff 
930*ba07fd2fSBen Hoff 	/* Reset per-channel toggles/counters */
931*ba07fd2fSBen Hoff 	WRITE_ONCE(v->last_buf_half_toggle, 0);
932*ba07fd2fSBen Hoff 	atomic_set(&v->sequence_number, 0);
933*ba07fd2fSBen Hoff 
934*ba07fd2fSBen Hoff 	mutex_unlock(&v->state_lock);
935*ba07fd2fSBen Hoff 
936*ba07fd2fSBen Hoff 	list_for_each_entry_safe(b, tmp, &done, list) {
937*ba07fd2fSBen Hoff 		list_del_init(&b->list);
938*ba07fd2fSBen Hoff 		vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR);
939*ba07fd2fSBen Hoff 	}
940*ba07fd2fSBen Hoff }
941*ba07fd2fSBen Hoff 
942*ba07fd2fSBen Hoff static void update_live_resolution(struct hws_pcie_dev *pdx, unsigned int ch,
943*ba07fd2fSBen Hoff 				   bool interlace)
944*ba07fd2fSBen Hoff {
945*ba07fd2fSBen Hoff 	u32 reg = readl(pdx->bar0_base + HWS_REG_IN_RES(ch));
946*ba07fd2fSBen Hoff 	u32 fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(ch));
947*ba07fd2fSBen Hoff 	u16 res_w = reg & 0xFFFF;
948*ba07fd2fSBen Hoff 	u16 res_h = (reg >> 16) & 0xFFFF;
949*ba07fd2fSBen Hoff 	struct hws_video *vid = &pdx->video[ch];
950*ba07fd2fSBen Hoff 	bool geometry_changed;
951*ba07fd2fSBen Hoff 	bool fps_changed;
952*ba07fd2fSBen Hoff 
953*ba07fd2fSBen Hoff 	bool within_hw = (res_w <= MAX_VIDEO_HW_W) &&
954*ba07fd2fSBen Hoff 	    ((!interlace && res_h <= MAX_VIDEO_HW_H) ||
955*ba07fd2fSBen Hoff 	     (interlace && (res_h * 2) <= MAX_VIDEO_HW_H));
956*ba07fd2fSBen Hoff 
957*ba07fd2fSBen Hoff 	if (!within_hw)
958*ba07fd2fSBen Hoff 		return;
959*ba07fd2fSBen Hoff 
960*ba07fd2fSBen Hoff 	geometry_changed = res_w != vid->pix.width ||
961*ba07fd2fSBen Hoff 		res_h != vid->pix.height ||
962*ba07fd2fSBen Hoff 		interlace != vid->pix.interlaced;
963*ba07fd2fSBen Hoff 	fps_changed = fps && fps != 0xFFFFFFFF && fps <= 240 &&
964*ba07fd2fSBen Hoff 		fps != vid->current_fps;
965*ba07fd2fSBen Hoff 
966*ba07fd2fSBen Hoff 	if (geometry_changed || fps_changed)
967*ba07fd2fSBen Hoff 		hws_video_apply_mode_change(pdx, ch, res_w, res_h, interlace,
968*ba07fd2fSBen Hoff 					    fps);
969*ba07fd2fSBen Hoff }
970*ba07fd2fSBen Hoff 
971*ba07fd2fSBen Hoff static int hws_open(struct file *file)
972*ba07fd2fSBen Hoff {
973*ba07fd2fSBen Hoff 	return v4l2_fh_open(file);
974*ba07fd2fSBen Hoff }
975*ba07fd2fSBen Hoff 
976*ba07fd2fSBen Hoff static const struct v4l2_file_operations hws_fops = {
977*ba07fd2fSBen Hoff 	.owner = THIS_MODULE,
978*ba07fd2fSBen Hoff 	.open = hws_open,
979*ba07fd2fSBen Hoff 	.release = vb2_fop_release,
980*ba07fd2fSBen Hoff 	.poll = vb2_fop_poll,
981*ba07fd2fSBen Hoff 	.unlocked_ioctl = video_ioctl2,
982*ba07fd2fSBen Hoff 	.mmap = vb2_fop_mmap,
983*ba07fd2fSBen Hoff };
984*ba07fd2fSBen Hoff 
985*ba07fd2fSBen Hoff static int hws_subscribe_event(struct v4l2_fh *fh,
986*ba07fd2fSBen Hoff 			       const struct v4l2_event_subscription *sub)
987*ba07fd2fSBen Hoff {
988*ba07fd2fSBen Hoff 	switch (sub->type) {
989*ba07fd2fSBen Hoff 	case V4L2_EVENT_SOURCE_CHANGE:
990*ba07fd2fSBen Hoff 		return v4l2_src_change_event_subscribe(fh, sub);
991*ba07fd2fSBen Hoff 	case V4L2_EVENT_CTRL:
992*ba07fd2fSBen Hoff 		return v4l2_ctrl_subscribe_event(fh, sub);
993*ba07fd2fSBen Hoff 	default:
994*ba07fd2fSBen Hoff 		return -EINVAL;
995*ba07fd2fSBen Hoff 	}
996*ba07fd2fSBen Hoff }
997*ba07fd2fSBen Hoff 
998*ba07fd2fSBen Hoff static const struct v4l2_ioctl_ops hws_ioctl_fops = {
999*ba07fd2fSBen Hoff 	/* Core caps/info */
1000*ba07fd2fSBen Hoff 	.vidioc_querycap = hws_vidioc_querycap,
1001*ba07fd2fSBen Hoff 
1002*ba07fd2fSBen Hoff 	/* Pixel format: still needed to report YUYV etc. */
1003*ba07fd2fSBen Hoff 	.vidioc_enum_fmt_vid_cap = hws_vidioc_enum_fmt_vid_cap,
1004*ba07fd2fSBen Hoff 	.vidioc_g_fmt_vid_cap = hws_vidioc_g_fmt_vid_cap,
1005*ba07fd2fSBen Hoff 	.vidioc_s_fmt_vid_cap = hws_vidioc_s_fmt_vid_cap,
1006*ba07fd2fSBen Hoff 	.vidioc_try_fmt_vid_cap = hws_vidioc_try_fmt_vid_cap,
1007*ba07fd2fSBen Hoff 
1008*ba07fd2fSBen Hoff 	/* Buffer queueing / streaming */
1009*ba07fd2fSBen Hoff 	.vidioc_reqbufs = vb2_ioctl_reqbufs,
1010*ba07fd2fSBen Hoff 	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
1011*ba07fd2fSBen Hoff 	.vidioc_create_bufs = vb2_ioctl_create_bufs,
1012*ba07fd2fSBen Hoff 	.vidioc_querybuf = vb2_ioctl_querybuf,
1013*ba07fd2fSBen Hoff 	.vidioc_qbuf = vb2_ioctl_qbuf,
1014*ba07fd2fSBen Hoff 	.vidioc_dqbuf = vb2_ioctl_dqbuf,
1015*ba07fd2fSBen Hoff 	.vidioc_expbuf = vb2_ioctl_expbuf,
1016*ba07fd2fSBen Hoff 	.vidioc_streamon = vb2_ioctl_streamon,
1017*ba07fd2fSBen Hoff 	.vidioc_streamoff = vb2_ioctl_streamoff,
1018*ba07fd2fSBen Hoff 
1019*ba07fd2fSBen Hoff 	/* Inputs */
1020*ba07fd2fSBen Hoff 	.vidioc_enum_input = hws_vidioc_enum_input,
1021*ba07fd2fSBen Hoff 	.vidioc_g_input = hws_vidioc_g_input,
1022*ba07fd2fSBen Hoff 	.vidioc_s_input = hws_vidioc_s_input,
1023*ba07fd2fSBen Hoff 
1024*ba07fd2fSBen Hoff 	/* DV timings (HDMI/DVI/VESA modes) */
1025*ba07fd2fSBen Hoff 	.vidioc_query_dv_timings = hws_vidioc_query_dv_timings,
1026*ba07fd2fSBen Hoff 	.vidioc_enum_dv_timings = hws_vidioc_enum_dv_timings,
1027*ba07fd2fSBen Hoff 	.vidioc_g_dv_timings = hws_vidioc_g_dv_timings,
1028*ba07fd2fSBen Hoff 	.vidioc_s_dv_timings = hws_vidioc_s_dv_timings,
1029*ba07fd2fSBen Hoff 	.vidioc_dv_timings_cap = hws_vidioc_dv_timings_cap,
1030*ba07fd2fSBen Hoff 
1031*ba07fd2fSBen Hoff 	.vidioc_log_status = v4l2_ctrl_log_status,
1032*ba07fd2fSBen Hoff 	.vidioc_subscribe_event = hws_subscribe_event,
1033*ba07fd2fSBen Hoff 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
1034*ba07fd2fSBen Hoff 	.vidioc_g_parm = hws_vidioc_g_parm,
1035*ba07fd2fSBen Hoff };
1036*ba07fd2fSBen Hoff 
1037*ba07fd2fSBen Hoff static u32 hws_calc_sizeimage(struct hws_video *v, u16 w, u16 h,
1038*ba07fd2fSBen Hoff 			      bool interlaced)
1039*ba07fd2fSBen Hoff {
1040*ba07fd2fSBen Hoff 	/* HWS captures packed YUYV only; stride is 16 bpp aligned to 64 bytes. */
1041*ba07fd2fSBen Hoff 	u32 lines = h;		/* full frame lines for sizeimage */
1042*ba07fd2fSBen Hoff 	u32 bytesperline = ALIGN(w * 2, 64);
1043*ba07fd2fSBen Hoff 	u32 sizeimage, half0;
1044*ba07fd2fSBen Hoff 
1045*ba07fd2fSBen Hoff 	/* publish into pix, since we now carry these in-state */
1046*ba07fd2fSBen Hoff 	v->pix.bytesperline = bytesperline;
1047*ba07fd2fSBen Hoff 	sizeimage = bytesperline * lines;
1048*ba07fd2fSBen Hoff 
1049*ba07fd2fSBen Hoff 	half0 = sizeimage / 2;
1050*ba07fd2fSBen Hoff 
1051*ba07fd2fSBen Hoff 	v->pix.sizeimage = sizeimage;
1052*ba07fd2fSBen Hoff 	v->pix.half_size = half0;	/* first half; second = sizeimage - half0 */
1053*ba07fd2fSBen Hoff 	v->pix.field = interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
1054*ba07fd2fSBen Hoff 
1055*ba07fd2fSBen Hoff 	return v->pix.sizeimage;
1056*ba07fd2fSBen Hoff }
1057*ba07fd2fSBen Hoff 
1058*ba07fd2fSBen Hoff static int hws_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
1059*ba07fd2fSBen Hoff 			   unsigned int *nplanes, unsigned int sizes[],
1060*ba07fd2fSBen Hoff 			   struct device *alloc_devs[])
1061*ba07fd2fSBen Hoff {
1062*ba07fd2fSBen Hoff 	struct hws_video *vid = q->drv_priv;
1063*ba07fd2fSBen Hoff 
1064*ba07fd2fSBen Hoff 	if (*nplanes) {
1065*ba07fd2fSBen Hoff 		if (sizes[0] < vid->pix.sizeimage)
1066*ba07fd2fSBen Hoff 			return -EINVAL;
1067*ba07fd2fSBen Hoff 	} else {
1068*ba07fd2fSBen Hoff 		*nplanes = 1;
1069*ba07fd2fSBen Hoff 		sizes[0] = vid->pix.sizeimage;
1070*ba07fd2fSBen Hoff 	}
1071*ba07fd2fSBen Hoff 
1072*ba07fd2fSBen Hoff 	return 0;
1073*ba07fd2fSBen Hoff }
1074*ba07fd2fSBen Hoff 
1075*ba07fd2fSBen Hoff static int hws_buffer_prepare(struct vb2_buffer *vb)
1076*ba07fd2fSBen Hoff {
1077*ba07fd2fSBen Hoff 	struct hws_video *vid = vb->vb2_queue->drv_priv;
1078*ba07fd2fSBen Hoff 	struct hws_pcie_dev *hws = vid->parent;
1079*ba07fd2fSBen Hoff 	size_t need = vid->pix.sizeimage;
1080*ba07fd2fSBen Hoff 	dma_addr_t dma_addr;
1081*ba07fd2fSBen Hoff 
1082*ba07fd2fSBen Hoff 	if (vb2_plane_size(vb, 0) < need)
1083*ba07fd2fSBen Hoff 		return -EINVAL;
1084*ba07fd2fSBen Hoff 
1085*ba07fd2fSBen Hoff 	/* Validate DMA address alignment */
1086*ba07fd2fSBen Hoff 	dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
1087*ba07fd2fSBen Hoff 	if (dma_addr & 0x3F) {	/* 64-byte alignment required */
1088*ba07fd2fSBen Hoff 		dev_err(&hws->pdev->dev,
1089*ba07fd2fSBen Hoff 			"Buffer DMA address 0x%llx not 64-byte aligned\n",
1090*ba07fd2fSBen Hoff 			(unsigned long long)dma_addr);
1091*ba07fd2fSBen Hoff 		return -EINVAL;
1092*ba07fd2fSBen Hoff 	}
1093*ba07fd2fSBen Hoff 
1094*ba07fd2fSBen Hoff 	vb2_set_plane_payload(vb, 0, need);
1095*ba07fd2fSBen Hoff 	return 0;
1096*ba07fd2fSBen Hoff }
1097*ba07fd2fSBen Hoff 
1098*ba07fd2fSBen Hoff static void hws_buffer_queue(struct vb2_buffer *vb)
1099*ba07fd2fSBen Hoff {
1100*ba07fd2fSBen Hoff 	struct hws_video *vid = vb->vb2_queue->drv_priv;
1101*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *buf = to_hwsbuf(vb);
1102*ba07fd2fSBen Hoff 	struct hws_pcie_dev *hws = vid->parent;
1103*ba07fd2fSBen Hoff 	unsigned long flags;
1104*ba07fd2fSBen Hoff 
1105*ba07fd2fSBen Hoff 	dev_dbg(&hws->pdev->dev,
1106*ba07fd2fSBen Hoff 		"buffer_queue(ch=%u): vb=%p sizeimage=%u q_active=%d\n",
1107*ba07fd2fSBen Hoff 		vid->channel_index, vb, vid->pix.sizeimage,
1108*ba07fd2fSBen Hoff 		READ_ONCE(vid->cap_active));
1109*ba07fd2fSBen Hoff 
1110*ba07fd2fSBen Hoff 	/* Initialize buffer slot */
1111*ba07fd2fSBen Hoff 	buf->slot = 0;
1112*ba07fd2fSBen Hoff 
1113*ba07fd2fSBen Hoff 	spin_lock_irqsave(&vid->irq_lock, flags);
1114*ba07fd2fSBen Hoff 	list_add_tail(&buf->list, &vid->capture_queue);
1115*ba07fd2fSBen Hoff 	vid->queued_count++;
1116*ba07fd2fSBen Hoff 
1117*ba07fd2fSBen Hoff 	/* If streaming and no in-flight buffer, prime HW immediately */
1118*ba07fd2fSBen Hoff 	if (READ_ONCE(vid->cap_active) && !vid->active) {
1119*ba07fd2fSBen Hoff 		dma_addr_t dma_addr;
1120*ba07fd2fSBen Hoff 
1121*ba07fd2fSBen Hoff 		dev_dbg(&hws->pdev->dev,
1122*ba07fd2fSBen Hoff 			"buffer_queue(ch=%u): priming first vb=%p\n",
1123*ba07fd2fSBen Hoff 			vid->channel_index, &buf->vb.vb2_buf);
1124*ba07fd2fSBen Hoff 		list_del_init(&buf->list);
1125*ba07fd2fSBen Hoff 		vid->queued_count--;
1126*ba07fd2fSBen Hoff 		vid->active = buf;
1127*ba07fd2fSBen Hoff 
1128*ba07fd2fSBen Hoff 		dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
1129*ba07fd2fSBen Hoff 		hws_program_dma_for_addr(vid->parent, vid->channel_index,
1130*ba07fd2fSBen Hoff 					 dma_addr);
1131*ba07fd2fSBen Hoff 		iowrite32(lower_32_bits(dma_addr),
1132*ba07fd2fSBen Hoff 			  hws->bar0_base + HWS_REG_DMA_ADDR(vid->channel_index));
1133*ba07fd2fSBen Hoff 
1134*ba07fd2fSBen Hoff 		wmb(); /* ensure descriptors visible before enabling capture */
1135*ba07fd2fSBen Hoff 		hws_enable_video_capture(hws, vid->channel_index, true);
1136*ba07fd2fSBen Hoff 		hws_prime_next_locked(vid);
1137*ba07fd2fSBen Hoff 	} else if (READ_ONCE(vid->cap_active) && vid->active) {
1138*ba07fd2fSBen Hoff 		hws_prime_next_locked(vid);
1139*ba07fd2fSBen Hoff 	}
1140*ba07fd2fSBen Hoff 	spin_unlock_irqrestore(&vid->irq_lock, flags);
1141*ba07fd2fSBen Hoff }
1142*ba07fd2fSBen Hoff 
1143*ba07fd2fSBen Hoff static int hws_start_streaming(struct vb2_queue *q, unsigned int count)
1144*ba07fd2fSBen Hoff {
1145*ba07fd2fSBen Hoff 	struct hws_video *v = q->drv_priv;
1146*ba07fd2fSBen Hoff 	struct hws_pcie_dev *hws = v->parent;
1147*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *to_program = NULL;	/* local copy */
1148*ba07fd2fSBen Hoff 	struct vb2_buffer *prog_vb2 = NULL;
1149*ba07fd2fSBen Hoff 	unsigned long flags;
1150*ba07fd2fSBen Hoff 	int ret;
1151*ba07fd2fSBen Hoff 
1152*ba07fd2fSBen Hoff 	dev_dbg(&hws->pdev->dev, "start_streaming: ch=%u count=%u\n",
1153*ba07fd2fSBen Hoff 		v->channel_index, count);
1154*ba07fd2fSBen Hoff 
1155*ba07fd2fSBen Hoff 	ret = hws_check_card_status(hws);
1156*ba07fd2fSBen Hoff 	if (ret) {
1157*ba07fd2fSBen Hoff 		struct hwsvideo_buffer *b, *tmp;
1158*ba07fd2fSBen Hoff 		unsigned long f;
1159*ba07fd2fSBen Hoff 		LIST_HEAD(queued);
1160*ba07fd2fSBen Hoff 
1161*ba07fd2fSBen Hoff 		spin_lock_irqsave(&v->irq_lock, f);
1162*ba07fd2fSBen Hoff 		if (v->active) {
1163*ba07fd2fSBen Hoff 			list_add_tail(&v->active->list, &queued);
1164*ba07fd2fSBen Hoff 			v->active = NULL;
1165*ba07fd2fSBen Hoff 		}
1166*ba07fd2fSBen Hoff 		if (v->next_prepared) {
1167*ba07fd2fSBen Hoff 			list_add_tail(&v->next_prepared->list, &queued);
1168*ba07fd2fSBen Hoff 			v->next_prepared = NULL;
1169*ba07fd2fSBen Hoff 		}
1170*ba07fd2fSBen Hoff 		while (!list_empty(&v->capture_queue)) {
1171*ba07fd2fSBen Hoff 			b = list_first_entry(&v->capture_queue,
1172*ba07fd2fSBen Hoff 					     struct hwsvideo_buffer, list);
1173*ba07fd2fSBen Hoff 			list_move_tail(&b->list, &queued);
1174*ba07fd2fSBen Hoff 		}
1175*ba07fd2fSBen Hoff 		spin_unlock_irqrestore(&v->irq_lock, f);
1176*ba07fd2fSBen Hoff 
1177*ba07fd2fSBen Hoff 		list_for_each_entry_safe(b, tmp, &queued, list) {
1178*ba07fd2fSBen Hoff 			list_del_init(&b->list);
1179*ba07fd2fSBen Hoff 			vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
1180*ba07fd2fSBen Hoff 		}
1181*ba07fd2fSBen Hoff 		return ret;
1182*ba07fd2fSBen Hoff 	}
1183*ba07fd2fSBen Hoff 	(void)hws_read_active_state(hws, v->channel_index,
1184*ba07fd2fSBen Hoff 				       &v->pix.interlaced);
1185*ba07fd2fSBen Hoff 
1186*ba07fd2fSBen Hoff 	lockdep_assert_held(&v->state_lock);
1187*ba07fd2fSBen Hoff 	/* init per-stream state */
1188*ba07fd2fSBen Hoff 	WRITE_ONCE(v->stop_requested, false);
1189*ba07fd2fSBen Hoff 	WRITE_ONCE(v->cap_active, true);
1190*ba07fd2fSBen Hoff 	WRITE_ONCE(v->half_seen, false);
1191*ba07fd2fSBen Hoff 	WRITE_ONCE(v->last_buf_half_toggle, 0);
1192*ba07fd2fSBen Hoff 
1193*ba07fd2fSBen Hoff 	/* Try to prime a buffer, but it's OK if none are queued yet */
1194*ba07fd2fSBen Hoff 	spin_lock_irqsave(&v->irq_lock, flags);
1195*ba07fd2fSBen Hoff 	if (!v->active && !list_empty(&v->capture_queue)) {
1196*ba07fd2fSBen Hoff 		to_program = list_first_entry(&v->capture_queue,
1197*ba07fd2fSBen Hoff 					      struct hwsvideo_buffer, list);
1198*ba07fd2fSBen Hoff 		list_del_init(&to_program->list);
1199*ba07fd2fSBen Hoff 		v->queued_count--;
1200*ba07fd2fSBen Hoff 		v->active = to_program;
1201*ba07fd2fSBen Hoff 		prog_vb2 = &to_program->vb.vb2_buf;
1202*ba07fd2fSBen Hoff 		dev_dbg(&hws->pdev->dev,
1203*ba07fd2fSBen Hoff 			"start_streaming: ch=%u took buffer %p\n",
1204*ba07fd2fSBen Hoff 			v->channel_index, to_program);
1205*ba07fd2fSBen Hoff 	}
1206*ba07fd2fSBen Hoff 	spin_unlock_irqrestore(&v->irq_lock, flags);
1207*ba07fd2fSBen Hoff 
1208*ba07fd2fSBen Hoff 	/* Only program/enable HW if we actually have a buffer */
1209*ba07fd2fSBen Hoff 	if (to_program) {
1210*ba07fd2fSBen Hoff 		if (!prog_vb2)
1211*ba07fd2fSBen Hoff 			prog_vb2 = &to_program->vb.vb2_buf;
1212*ba07fd2fSBen Hoff 		{
1213*ba07fd2fSBen Hoff 			dma_addr_t dma_addr;
1214*ba07fd2fSBen Hoff 
1215*ba07fd2fSBen Hoff 			dma_addr = vb2_dma_contig_plane_dma_addr(prog_vb2, 0);
1216*ba07fd2fSBen Hoff 			hws_program_dma_for_addr(hws, v->channel_index, dma_addr);
1217*ba07fd2fSBen Hoff 			iowrite32(lower_32_bits(dma_addr),
1218*ba07fd2fSBen Hoff 				  hws->bar0_base +
1219*ba07fd2fSBen Hoff 				  HWS_REG_DMA_ADDR(v->channel_index));
1220*ba07fd2fSBen Hoff 			dev_dbg(&hws->pdev->dev,
1221*ba07fd2fSBen Hoff 				"start_streaming: ch=%u programmed buffer %p dma=0x%08x\n",
1222*ba07fd2fSBen Hoff 				v->channel_index, to_program,
1223*ba07fd2fSBen Hoff 				lower_32_bits(dma_addr));
1224*ba07fd2fSBen Hoff 			(void)readl(hws->bar0_base + HWS_REG_INT_STATUS);
1225*ba07fd2fSBen Hoff 		}
1226*ba07fd2fSBen Hoff 
1227*ba07fd2fSBen Hoff 		wmb(); /* ensure descriptors visible before enabling capture */
1228*ba07fd2fSBen Hoff 		hws_enable_video_capture(hws, v->channel_index, true);
1229*ba07fd2fSBen Hoff 		{
1230*ba07fd2fSBen Hoff 			unsigned long pf;
1231*ba07fd2fSBen Hoff 
1232*ba07fd2fSBen Hoff 			spin_lock_irqsave(&v->irq_lock, pf);
1233*ba07fd2fSBen Hoff 			hws_prime_next_locked(v);
1234*ba07fd2fSBen Hoff 			spin_unlock_irqrestore(&v->irq_lock, pf);
1235*ba07fd2fSBen Hoff 		}
1236*ba07fd2fSBen Hoff 	} else {
1237*ba07fd2fSBen Hoff 		dev_dbg(&hws->pdev->dev,
1238*ba07fd2fSBen Hoff 			"start_streaming: ch=%u no buffer yet (will arm on QBUF)\n",
1239*ba07fd2fSBen Hoff 			v->channel_index);
1240*ba07fd2fSBen Hoff 	}
1241*ba07fd2fSBen Hoff 
1242*ba07fd2fSBen Hoff 	return 0;
1243*ba07fd2fSBen Hoff }
1244*ba07fd2fSBen Hoff 
1245*ba07fd2fSBen Hoff static void hws_log_video_state(struct hws_video *v, const char *action,
1246*ba07fd2fSBen Hoff 				const char *phase)
1247*ba07fd2fSBen Hoff {
1248*ba07fd2fSBen Hoff 	struct hws_pcie_dev *hws = v->parent;
1249*ba07fd2fSBen Hoff 	unsigned long flags;
1250*ba07fd2fSBen Hoff 	unsigned int queued = 0;
1251*ba07fd2fSBen Hoff 	unsigned int tracked = 0;
1252*ba07fd2fSBen Hoff 	unsigned int seq = 0;
1253*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *b;
1254*ba07fd2fSBen Hoff 	bool streaming = vb2_is_streaming(&v->buffer_queue);
1255*ba07fd2fSBen Hoff 	bool cap_active;
1256*ba07fd2fSBen Hoff 	bool stop_requested;
1257*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *active;
1258*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *next_prepared;
1259*ba07fd2fSBen Hoff 
1260*ba07fd2fSBen Hoff 	spin_lock_irqsave(&v->irq_lock, flags);
1261*ba07fd2fSBen Hoff 	list_for_each_entry(b, &v->capture_queue, list)
1262*ba07fd2fSBen Hoff 		queued++;
1263*ba07fd2fSBen Hoff 	cap_active = READ_ONCE(v->cap_active);
1264*ba07fd2fSBen Hoff 	stop_requested = READ_ONCE(v->stop_requested);
1265*ba07fd2fSBen Hoff 	active = v->active;
1266*ba07fd2fSBen Hoff 	next_prepared = v->next_prepared;
1267*ba07fd2fSBen Hoff 	tracked = v->queued_count;
1268*ba07fd2fSBen Hoff 	seq = (u32)atomic_read(&v->sequence_number);
1269*ba07fd2fSBen Hoff 	spin_unlock_irqrestore(&v->irq_lock, flags);
1270*ba07fd2fSBen Hoff 
1271*ba07fd2fSBen Hoff 	dev_dbg(&hws->pdev->dev,
1272*ba07fd2fSBen Hoff 		"video:%s:%s ch=%u streaming=%d cap=%d stop=%d active=%p next=%p queued=%u tracked=%u seq=%u\n",
1273*ba07fd2fSBen Hoff 		action, phase, v->channel_index, streaming, cap_active,
1274*ba07fd2fSBen Hoff 		stop_requested, active, next_prepared, queued, tracked, seq);
1275*ba07fd2fSBen Hoff }
1276*ba07fd2fSBen Hoff 
1277*ba07fd2fSBen Hoff static void hws_stop_streaming(struct vb2_queue *q)
1278*ba07fd2fSBen Hoff {
1279*ba07fd2fSBen Hoff 	struct hws_video *v = q->drv_priv;
1280*ba07fd2fSBen Hoff 	struct hws_pcie_dev *hws = v->parent;
1281*ba07fd2fSBen Hoff 	unsigned long flags;
1282*ba07fd2fSBen Hoff 	struct hwsvideo_buffer *b, *tmp;
1283*ba07fd2fSBen Hoff 	LIST_HEAD(done);
1284*ba07fd2fSBen Hoff 	unsigned int done_cnt = 0;
1285*ba07fd2fSBen Hoff 	u64 start_ns = ktime_get_mono_fast_ns();
1286*ba07fd2fSBen Hoff 
1287*ba07fd2fSBen Hoff 	hws_log_video_state(v, "streamoff", "begin");
1288*ba07fd2fSBen Hoff 
1289*ba07fd2fSBen Hoff 	/* 1) Quiesce SW/HW first */
1290*ba07fd2fSBen Hoff 	lockdep_assert_held(&v->state_lock);
1291*ba07fd2fSBen Hoff 	WRITE_ONCE(v->cap_active, false);
1292*ba07fd2fSBen Hoff 	WRITE_ONCE(v->stop_requested, true);
1293*ba07fd2fSBen Hoff 
1294*ba07fd2fSBen Hoff 	hws_enable_video_capture(v->parent, v->channel_index, false);
1295*ba07fd2fSBen Hoff 
1296*ba07fd2fSBen Hoff 	/* 2) Collect in-flight + queued under the IRQ lock */
1297*ba07fd2fSBen Hoff 	spin_lock_irqsave(&v->irq_lock, flags);
1298*ba07fd2fSBen Hoff 	hws_video_collect_done_locked(v, &done);
1299*ba07fd2fSBen Hoff 	spin_unlock_irqrestore(&v->irq_lock, flags);
1300*ba07fd2fSBen Hoff 
1301*ba07fd2fSBen Hoff 	/* 3) Complete outside the lock */
1302*ba07fd2fSBen Hoff 	list_for_each_entry_safe(b, tmp, &done, list) {
1303*ba07fd2fSBen Hoff 		/* Unlink from 'done' before completing */
1304*ba07fd2fSBen Hoff 		list_del_init(&b->list);
1305*ba07fd2fSBen Hoff 		vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR);
1306*ba07fd2fSBen Hoff 		done_cnt++;
1307*ba07fd2fSBen Hoff 	}
1308*ba07fd2fSBen Hoff 	dev_dbg(&hws->pdev->dev,
1309*ba07fd2fSBen Hoff 		"video:streamoff:done ch=%u completed=%u (%lluus)\n",
1310*ba07fd2fSBen Hoff 		v->channel_index, done_cnt, hws_elapsed_us(start_ns));
1311*ba07fd2fSBen Hoff 	hws_log_video_state(v, "streamoff", "end");
1312*ba07fd2fSBen Hoff }
1313*ba07fd2fSBen Hoff 
1314*ba07fd2fSBen Hoff static const struct vb2_ops hwspcie_video_qops = {
1315*ba07fd2fSBen Hoff 	.queue_setup = hws_queue_setup,
1316*ba07fd2fSBen Hoff 	.buf_prepare = hws_buffer_prepare,
1317*ba07fd2fSBen Hoff 	.buf_init = hws_buf_init,
1318*ba07fd2fSBen Hoff 	.buf_finish = hws_buf_finish,
1319*ba07fd2fSBen Hoff 	.buf_cleanup = hws_buf_cleanup,
1320*ba07fd2fSBen Hoff 	.buf_queue = hws_buffer_queue,
1321*ba07fd2fSBen Hoff 	.start_streaming = hws_start_streaming,
1322*ba07fd2fSBen Hoff 	.stop_streaming = hws_stop_streaming,
1323*ba07fd2fSBen Hoff };
1324*ba07fd2fSBen Hoff 
1325*ba07fd2fSBen Hoff int hws_video_register(struct hws_pcie_dev *dev)
1326*ba07fd2fSBen Hoff {
1327*ba07fd2fSBen Hoff 	int i, ret;
1328*ba07fd2fSBen Hoff 
1329*ba07fd2fSBen Hoff 	ret = v4l2_device_register(&dev->pdev->dev, &dev->v4l2_device);
1330*ba07fd2fSBen Hoff 	if (ret) {
1331*ba07fd2fSBen Hoff 		dev_err(&dev->pdev->dev, "v4l2_device_register failed: %d\n",
1332*ba07fd2fSBen Hoff 			ret);
1333*ba07fd2fSBen Hoff 		return ret;
1334*ba07fd2fSBen Hoff 	}
1335*ba07fd2fSBen Hoff 
1336*ba07fd2fSBen Hoff 	for (i = 0; i < dev->cur_max_video_ch; i++) {
1337*ba07fd2fSBen Hoff 		struct hws_video *ch = &dev->video[i];
1338*ba07fd2fSBen Hoff 		struct video_device *vdev;
1339*ba07fd2fSBen Hoff 		struct vb2_queue *q;
1340*ba07fd2fSBen Hoff 
1341*ba07fd2fSBen Hoff 		/* hws_video_init_channel() should have set:
1342*ba07fd2fSBen Hoff 		 * - ch->parent, ch->channel_index
1343*ba07fd2fSBen Hoff 		 * - locks (state_lock, irq_lock)
1344*ba07fd2fSBen Hoff 		 * - capture_queue (INIT_LIST_HEAD)
1345*ba07fd2fSBen Hoff 		 * - control_handler + controls
1346*ba07fd2fSBen Hoff 		 * - fmt_curr (width/height)
1347*ba07fd2fSBen Hoff 		 * Do not reinitialize any of those here.
1348*ba07fd2fSBen Hoff 		 */
1349*ba07fd2fSBen Hoff 
1350*ba07fd2fSBen Hoff 		vdev = video_device_alloc();
1351*ba07fd2fSBen Hoff 		if (!vdev) {
1352*ba07fd2fSBen Hoff 			dev_err(&dev->pdev->dev,
1353*ba07fd2fSBen Hoff 				"video_device_alloc ch%u failed\n", i);
1354*ba07fd2fSBen Hoff 			ret = -ENOMEM;
1355*ba07fd2fSBen Hoff 			goto err_unwind;
1356*ba07fd2fSBen Hoff 		}
1357*ba07fd2fSBen Hoff 		ch->video_device = vdev;
1358*ba07fd2fSBen Hoff 
1359*ba07fd2fSBen Hoff 		/* Basic V4L2 node setup */
1360*ba07fd2fSBen Hoff 		snprintf(vdev->name, sizeof(vdev->name), "%s-hdmi%u",
1361*ba07fd2fSBen Hoff 			 KBUILD_MODNAME, i);
1362*ba07fd2fSBen Hoff 		vdev->v4l2_dev = &dev->v4l2_device;
1363*ba07fd2fSBen Hoff 		vdev->fops = &hws_fops;
1364*ba07fd2fSBen Hoff 		vdev->ioctl_ops = &hws_ioctl_fops;
1365*ba07fd2fSBen Hoff 		vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
1366*ba07fd2fSBen Hoff 		vdev->lock = &ch->state_lock;	/* serialize file ops */
1367*ba07fd2fSBen Hoff 		vdev->ctrl_handler = &ch->control_handler;
1368*ba07fd2fSBen Hoff 		vdev->vfl_dir = VFL_DIR_RX;
1369*ba07fd2fSBen Hoff 		vdev->release = video_device_release;
1370*ba07fd2fSBen Hoff 		if (ch->control_handler.error) {
1371*ba07fd2fSBen Hoff 			ret = ch->control_handler.error;
1372*ba07fd2fSBen Hoff 			goto err_unwind;
1373*ba07fd2fSBen Hoff 		}
1374*ba07fd2fSBen Hoff 		video_set_drvdata(vdev, ch);
1375*ba07fd2fSBen Hoff 
1376*ba07fd2fSBen Hoff 		/* vb2 queue init (dma-contig) */
1377*ba07fd2fSBen Hoff 		q = &ch->buffer_queue;
1378*ba07fd2fSBen Hoff 		memset(q, 0, sizeof(*q));
1379*ba07fd2fSBen Hoff 		q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1380*ba07fd2fSBen Hoff 		q->io_modes = VB2_MMAP | VB2_DMABUF;
1381*ba07fd2fSBen Hoff 		q->drv_priv = ch;
1382*ba07fd2fSBen Hoff 		q->buf_struct_size = sizeof(struct hwsvideo_buffer);
1383*ba07fd2fSBen Hoff 		q->ops = &hwspcie_video_qops;
1384*ba07fd2fSBen Hoff 		q->mem_ops = &vb2_dma_contig_memops;
1385*ba07fd2fSBen Hoff 		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1386*ba07fd2fSBen Hoff 		q->lock = &ch->state_lock;
1387*ba07fd2fSBen Hoff 		q->min_queued_buffers = 1;
1388*ba07fd2fSBen Hoff 		q->dev = &dev->pdev->dev;
1389*ba07fd2fSBen Hoff 
1390*ba07fd2fSBen Hoff 		ret = vb2_queue_init(q);
1391*ba07fd2fSBen Hoff 		vdev->queue = q;
1392*ba07fd2fSBen Hoff 		if (ret) {
1393*ba07fd2fSBen Hoff 			dev_err(&dev->pdev->dev,
1394*ba07fd2fSBen Hoff 				"vb2_queue_init ch%u failed: %d\n", i, ret);
1395*ba07fd2fSBen Hoff 			goto err_unwind;
1396*ba07fd2fSBen Hoff 		}
1397*ba07fd2fSBen Hoff 
1398*ba07fd2fSBen Hoff 		/* Make controls live (no-op if none or already set up) */
1399*ba07fd2fSBen Hoff 		if (ch->control_handler.error) {
1400*ba07fd2fSBen Hoff 			ret = ch->control_handler.error;
1401*ba07fd2fSBen Hoff 			dev_err(&dev->pdev->dev,
1402*ba07fd2fSBen Hoff 				"ctrl handler ch%u error: %d\n", i, ret);
1403*ba07fd2fSBen Hoff 			goto err_unwind;
1404*ba07fd2fSBen Hoff 		}
1405*ba07fd2fSBen Hoff 		v4l2_ctrl_handler_setup(&ch->control_handler);
1406*ba07fd2fSBen Hoff 		ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
1407*ba07fd2fSBen Hoff 		if (ret) {
1408*ba07fd2fSBen Hoff 			dev_err(&dev->pdev->dev,
1409*ba07fd2fSBen Hoff 				"video_register_device ch%u failed: %d\n", i,
1410*ba07fd2fSBen Hoff 				ret);
1411*ba07fd2fSBen Hoff 			goto err_unwind;
1412*ba07fd2fSBen Hoff 		}
1413*ba07fd2fSBen Hoff 	}
1414*ba07fd2fSBen Hoff 
1415*ba07fd2fSBen Hoff 	return 0;
1416*ba07fd2fSBen Hoff 
1417*ba07fd2fSBen Hoff err_unwind:
1418*ba07fd2fSBen Hoff 	for (; i >= 0; i--) {
1419*ba07fd2fSBen Hoff 		struct hws_video *ch = &dev->video[i];
1420*ba07fd2fSBen Hoff 
1421*ba07fd2fSBen Hoff 		hws_video_release_registration(ch);
1422*ba07fd2fSBen Hoff 	}
1423*ba07fd2fSBen Hoff 	v4l2_device_unregister(&dev->v4l2_device);
1424*ba07fd2fSBen Hoff 	return ret;
1425*ba07fd2fSBen Hoff }
1426*ba07fd2fSBen Hoff 
1427*ba07fd2fSBen Hoff void hws_video_unregister(struct hws_pcie_dev *dev)
1428*ba07fd2fSBen Hoff {
1429*ba07fd2fSBen Hoff 	int i;
1430*ba07fd2fSBen Hoff 
1431*ba07fd2fSBen Hoff 	if (!dev)
1432*ba07fd2fSBen Hoff 		return;
1433*ba07fd2fSBen Hoff 
1434*ba07fd2fSBen Hoff 	for (i = 0; i < dev->cur_max_video_ch; i++) {
1435*ba07fd2fSBen Hoff 		struct hws_video *ch = &dev->video[i];
1436*ba07fd2fSBen Hoff 
1437*ba07fd2fSBen Hoff 		hws_video_release_registration(ch);
1438*ba07fd2fSBen Hoff 		v4l2_ctrl_handler_free(&ch->control_handler);
1439*ba07fd2fSBen Hoff 	}
1440*ba07fd2fSBen Hoff 	v4l2_device_unregister(&dev->v4l2_device);
1441*ba07fd2fSBen Hoff }
1442*ba07fd2fSBen Hoff 
1443*ba07fd2fSBen Hoff int hws_video_quiesce(struct hws_pcie_dev *hws, const char *reason)
1444*ba07fd2fSBen Hoff {
1445*ba07fd2fSBen Hoff 	int i, ret = 0;
1446*ba07fd2fSBen Hoff 	u64 start_ns = ktime_get_mono_fast_ns();
1447*ba07fd2fSBen Hoff 
1448*ba07fd2fSBen Hoff 	dev_dbg(&hws->pdev->dev, "video:%s:begin channels=%u\n", reason,
1449*ba07fd2fSBen Hoff 		hws->cur_max_video_ch);
1450*ba07fd2fSBen Hoff 	for (i = 0; i < hws->cur_max_video_ch; i++) {
1451*ba07fd2fSBen Hoff 		struct hws_video *vid = &hws->video[i];
1452*ba07fd2fSBen Hoff 		struct vb2_queue *q = &vid->buffer_queue;
1453*ba07fd2fSBen Hoff 		u64 ch_start_ns = ktime_get_mono_fast_ns();
1454*ba07fd2fSBen Hoff 		bool streaming;
1455*ba07fd2fSBen Hoff 
1456*ba07fd2fSBen Hoff 		if (!q || !q->ops) {
1457*ba07fd2fSBen Hoff 			dev_dbg(&hws->pdev->dev,
1458*ba07fd2fSBen Hoff 				"video:%s:ch=%d skipped queue-unavailable\n",
1459*ba07fd2fSBen Hoff 				reason, i);
1460*ba07fd2fSBen Hoff 			continue;
1461*ba07fd2fSBen Hoff 		}
1462*ba07fd2fSBen Hoff 
1463*ba07fd2fSBen Hoff 		streaming = vb2_is_streaming(q);
1464*ba07fd2fSBen Hoff 		hws_log_video_state(vid, reason, "channel");
1465*ba07fd2fSBen Hoff 		if (streaming) {
1466*ba07fd2fSBen Hoff 			/* Stop via vb2, which runs .stop_streaming. */
1467*ba07fd2fSBen Hoff 			int r = vb2_streamoff(q, q->type);
1468*ba07fd2fSBen Hoff 
1469*ba07fd2fSBen Hoff 			dev_dbg(&hws->pdev->dev,
1470*ba07fd2fSBen Hoff 				"video:%s:ch=%d streamoff ret=%d (%lluus)\n",
1471*ba07fd2fSBen Hoff 				reason, i, r, hws_elapsed_us(ch_start_ns));
1472*ba07fd2fSBen Hoff 			if (r && !ret)
1473*ba07fd2fSBen Hoff 				ret = r;
1474*ba07fd2fSBen Hoff 		} else {
1475*ba07fd2fSBen Hoff 			dev_dbg(&hws->pdev->dev,
1476*ba07fd2fSBen Hoff 				"video:%s:ch=%d idle (%lluus)\n",
1477*ba07fd2fSBen Hoff 				reason, i, hws_elapsed_us(ch_start_ns));
1478*ba07fd2fSBen Hoff 		}
1479*ba07fd2fSBen Hoff 	}
1480*ba07fd2fSBen Hoff 	dev_dbg(&hws->pdev->dev, "video:%s:done ret=%d (%lluus)\n", reason,
1481*ba07fd2fSBen Hoff 		ret, hws_elapsed_us(start_ns));
1482*ba07fd2fSBen Hoff 	return ret;
1483*ba07fd2fSBen Hoff }
1484*ba07fd2fSBen Hoff 
1485*ba07fd2fSBen Hoff void hws_video_pm_resume(struct hws_pcie_dev *hws)
1486*ba07fd2fSBen Hoff {
1487*ba07fd2fSBen Hoff 	/* Nothing mandatory to do here for vb2; userspace will STREAMON
1488*ba07fd2fSBen Hoff 	 * again when ready.
1489*ba07fd2fSBen Hoff 	 */
1490*ba07fd2fSBen Hoff }
1491