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