1*ba07fd2fSBen Hoff // SPDX-License-Identifier: GPL-2.0-only 2*ba07fd2fSBen Hoff #include <linux/pci.h> 3*ba07fd2fSBen Hoff #include <linux/types.h> 4*ba07fd2fSBen Hoff #include <linux/iopoll.h> 5*ba07fd2fSBen Hoff #include <linux/bitfield.h> 6*ba07fd2fSBen Hoff #include <linux/module.h> 7*ba07fd2fSBen Hoff #include <linux/init.h> 8*ba07fd2fSBen Hoff #include <linux/kthread.h> 9*ba07fd2fSBen Hoff #include <linux/interrupt.h> 10*ba07fd2fSBen Hoff #include <linux/dma-mapping.h> 11*ba07fd2fSBen Hoff #include <linux/err.h> 12*ba07fd2fSBen Hoff #include <linux/ktime.h> 13*ba07fd2fSBen Hoff #include <linux/math64.h> 14*ba07fd2fSBen Hoff #include <linux/pm.h> 15*ba07fd2fSBen Hoff #include <linux/freezer.h> 16*ba07fd2fSBen Hoff #include <linux/pci_regs.h> 17*ba07fd2fSBen Hoff 18*ba07fd2fSBen Hoff #include <media/v4l2-ctrls.h> 19*ba07fd2fSBen Hoff 20*ba07fd2fSBen Hoff #include "hws.h" 21*ba07fd2fSBen Hoff #include "hws_reg.h" 22*ba07fd2fSBen Hoff #include "hws_video.h" 23*ba07fd2fSBen Hoff #include "hws_irq.h" 24*ba07fd2fSBen Hoff #include "hws_v4l2_ioctl.h" 25*ba07fd2fSBen Hoff 26*ba07fd2fSBen Hoff #define DRV_NAME "hws" 27*ba07fd2fSBen Hoff #define HWS_BUSY_POLL_DELAY_US 10 28*ba07fd2fSBen Hoff #define HWS_BUSY_POLL_TIMEOUT_US 1000000 29*ba07fd2fSBen Hoff 30*ba07fd2fSBen Hoff static unsigned long long hws_elapsed_us(u64 start_ns) 31*ba07fd2fSBen Hoff { 32*ba07fd2fSBen Hoff return div_u64(ktime_get_mono_fast_ns() - start_ns, 1000); 33*ba07fd2fSBen Hoff } 34*ba07fd2fSBen Hoff 35*ba07fd2fSBen Hoff /* register layout inside HWS_REG_DEVICE_INFO */ 36*ba07fd2fSBen Hoff #define DEVINFO_VER GENMASK(7, 0) 37*ba07fd2fSBen Hoff #define DEVINFO_SUBVER GENMASK(15, 8) 38*ba07fd2fSBen Hoff #define DEVINFO_YV12 GENMASK(31, 28) 39*ba07fd2fSBen Hoff #define DEVINFO_HWKEY GENMASK(27, 24) 40*ba07fd2fSBen Hoff #define DEVINFO_PORTID GENMASK(25, 24) /* low 2 bits of HW-key */ 41*ba07fd2fSBen Hoff 42*ba07fd2fSBen Hoff #define MAKE_ENTRY(__vend, __chip, __subven, __subdev, __configptr) \ 43*ba07fd2fSBen Hoff { .vendor = (__vend), \ 44*ba07fd2fSBen Hoff .device = (__chip), \ 45*ba07fd2fSBen Hoff .subvendor = (__subven), \ 46*ba07fd2fSBen Hoff .subdevice = (__subdev), \ 47*ba07fd2fSBen Hoff .driver_data = (unsigned long)(__configptr) } 48*ba07fd2fSBen Hoff 49*ba07fd2fSBen Hoff /* 50*ba07fd2fSBen Hoff * PCI IDs for HWS family cards. 51*ba07fd2fSBen Hoff * 52*ba07fd2fSBen Hoff * The subsystem IDs are fixed at 0x8888:0x0007 for this family. Some boards 53*ba07fd2fSBen Hoff * enumerate with vendor ID 0x8888 or 0x1f33. Exact SKU names are not fully 54*ba07fd2fSBen Hoff * pinned down yet; update these comments when vendor documentation or INF 55*ba07fd2fSBen Hoff * strings are available. 56*ba07fd2fSBen Hoff */ 57*ba07fd2fSBen Hoff static const struct pci_device_id hws_pci_table[] = { 58*ba07fd2fSBen Hoff /* HWS family, SKU unknown. */ 59*ba07fd2fSBen Hoff MAKE_ENTRY(0x8888, 0x9534, 0x8888, 0x0007, NULL), 60*ba07fd2fSBen Hoff MAKE_ENTRY(0x1F33, 0x8534, 0x8888, 0x0007, NULL), 61*ba07fd2fSBen Hoff MAKE_ENTRY(0x1F33, 0x8554, 0x8888, 0x0007, NULL), 62*ba07fd2fSBen Hoff 63*ba07fd2fSBen Hoff /* HWS 2x2 HDMI family. */ 64*ba07fd2fSBen Hoff MAKE_ENTRY(0x8888, 0x8524, 0x8888, 0x0007, NULL), 65*ba07fd2fSBen Hoff /* HWS 2x2 SDI family. */ 66*ba07fd2fSBen Hoff MAKE_ENTRY(0x1F33, 0x6524, 0x8888, 0x0007, NULL), 67*ba07fd2fSBen Hoff 68*ba07fd2fSBen Hoff /* HWS X4 HDMI family. */ 69*ba07fd2fSBen Hoff MAKE_ENTRY(0x8888, 0x8504, 0x8888, 0x0007, NULL), 70*ba07fd2fSBen Hoff /* HWS X4 SDI family. */ 71*ba07fd2fSBen Hoff MAKE_ENTRY(0x8888, 0x6504, 0x8888, 0x0007, NULL), 72*ba07fd2fSBen Hoff 73*ba07fd2fSBen Hoff /* HWS family, SKU unknown. */ 74*ba07fd2fSBen Hoff MAKE_ENTRY(0x8888, 0x8532, 0x8888, 0x0007, NULL), 75*ba07fd2fSBen Hoff MAKE_ENTRY(0x8888, 0x8512, 0x8888, 0x0007, NULL), 76*ba07fd2fSBen Hoff MAKE_ENTRY(0x8888, 0x8501, 0x8888, 0x0007, NULL), 77*ba07fd2fSBen Hoff MAKE_ENTRY(0x1F33, 0x6502, 0x8888, 0x0007, NULL), 78*ba07fd2fSBen Hoff 79*ba07fd2fSBen Hoff /* HWS X4 HDMI family (alternate vendor ID). */ 80*ba07fd2fSBen Hoff MAKE_ENTRY(0x1F33, 0x8504, 0x8888, 0x0007, NULL), 81*ba07fd2fSBen Hoff /* HWS 2x2 HDMI family (alternate vendor ID). */ 82*ba07fd2fSBen Hoff MAKE_ENTRY(0x1F33, 0x8524, 0x8888, 0x0007, NULL), 83*ba07fd2fSBen Hoff 84*ba07fd2fSBen Hoff {} 85*ba07fd2fSBen Hoff }; 86*ba07fd2fSBen Hoff 87*ba07fd2fSBen Hoff static void enable_pcie_relaxed_ordering(struct pci_dev *dev) 88*ba07fd2fSBen Hoff { 89*ba07fd2fSBen Hoff pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); 90*ba07fd2fSBen Hoff } 91*ba07fd2fSBen Hoff 92*ba07fd2fSBen Hoff static void hws_configure_hardware_capabilities(struct hws_pcie_dev *hdev) 93*ba07fd2fSBen Hoff { 94*ba07fd2fSBen Hoff u16 id = hdev->device_id; 95*ba07fd2fSBen Hoff 96*ba07fd2fSBen Hoff /* select per-chip channel counts */ 97*ba07fd2fSBen Hoff switch (id) { 98*ba07fd2fSBen Hoff case 0x9534: 99*ba07fd2fSBen Hoff case 0x6524: 100*ba07fd2fSBen Hoff case 0x8524: 101*ba07fd2fSBen Hoff case 0x8504: 102*ba07fd2fSBen Hoff case 0x6504: 103*ba07fd2fSBen Hoff hdev->cur_max_video_ch = 4; 104*ba07fd2fSBen Hoff break; 105*ba07fd2fSBen Hoff case 0x8532: 106*ba07fd2fSBen Hoff hdev->cur_max_video_ch = 2; 107*ba07fd2fSBen Hoff break; 108*ba07fd2fSBen Hoff case 0x8512: 109*ba07fd2fSBen Hoff case 0x6502: 110*ba07fd2fSBen Hoff hdev->cur_max_video_ch = 2; 111*ba07fd2fSBen Hoff break; 112*ba07fd2fSBen Hoff case 0x8501: 113*ba07fd2fSBen Hoff hdev->cur_max_video_ch = 1; 114*ba07fd2fSBen Hoff break; 115*ba07fd2fSBen Hoff default: 116*ba07fd2fSBen Hoff hdev->cur_max_video_ch = 4; 117*ba07fd2fSBen Hoff break; 118*ba07fd2fSBen Hoff } 119*ba07fd2fSBen Hoff 120*ba07fd2fSBen Hoff /* universal buffer capacity */ 121*ba07fd2fSBen Hoff hdev->max_hw_video_buf_sz = MAX_MM_VIDEO_SIZE; 122*ba07fd2fSBen Hoff 123*ba07fd2fSBen Hoff /* decide hardware-version and program DMA max size if needed */ 124*ba07fd2fSBen Hoff if (hdev->device_ver > 121) { 125*ba07fd2fSBen Hoff if (id == 0x8501 && hdev->device_ver == 122) { 126*ba07fd2fSBen Hoff hdev->hw_ver = 0; 127*ba07fd2fSBen Hoff } else { 128*ba07fd2fSBen Hoff hdev->hw_ver = 1; 129*ba07fd2fSBen Hoff u32 dma_max = (u32)(MAX_VIDEO_SCALER_SIZE / 16); 130*ba07fd2fSBen Hoff 131*ba07fd2fSBen Hoff writel(dma_max, hdev->bar0_base + HWS_REG_DMA_MAX_SIZE); 132*ba07fd2fSBen Hoff /* readback to flush posted MMIO write */ 133*ba07fd2fSBen Hoff (void)readl(hdev->bar0_base + HWS_REG_DMA_MAX_SIZE); 134*ba07fd2fSBen Hoff } 135*ba07fd2fSBen Hoff } else { 136*ba07fd2fSBen Hoff hdev->hw_ver = 0; 137*ba07fd2fSBen Hoff } 138*ba07fd2fSBen Hoff } 139*ba07fd2fSBen Hoff 140*ba07fd2fSBen Hoff static void hws_stop_device(struct hws_pcie_dev *hws); 141*ba07fd2fSBen Hoff 142*ba07fd2fSBen Hoff static void hws_log_lifecycle_snapshot(struct hws_pcie_dev *hws, 143*ba07fd2fSBen Hoff const char *action, 144*ba07fd2fSBen Hoff const char *phase) 145*ba07fd2fSBen Hoff { 146*ba07fd2fSBen Hoff struct device *dev; 147*ba07fd2fSBen Hoff u32 int_en, int_status, vcap, sys_status, dec_mode; 148*ba07fd2fSBen Hoff 149*ba07fd2fSBen Hoff if (!hws || !hws->pdev) 150*ba07fd2fSBen Hoff return; 151*ba07fd2fSBen Hoff 152*ba07fd2fSBen Hoff dev = &hws->pdev->dev; 153*ba07fd2fSBen Hoff if (!hws->bar0_base) { 154*ba07fd2fSBen Hoff dev_dbg(dev, 155*ba07fd2fSBen Hoff "lifecycle:%s:%s bar0-unmapped suspended=%d start_run=%d pci_lost=%d irq=%d\n", 156*ba07fd2fSBen Hoff action, phase, READ_ONCE(hws->suspended), hws->start_run, 157*ba07fd2fSBen Hoff hws->pci_lost, hws->irq); 158*ba07fd2fSBen Hoff return; 159*ba07fd2fSBen Hoff } 160*ba07fd2fSBen Hoff 161*ba07fd2fSBen Hoff int_en = readl(hws->bar0_base + INT_EN_REG_BASE); 162*ba07fd2fSBen Hoff int_status = readl(hws->bar0_base + HWS_REG_INT_STATUS); 163*ba07fd2fSBen Hoff vcap = readl(hws->bar0_base + HWS_REG_VCAP_ENABLE); 164*ba07fd2fSBen Hoff sys_status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); 165*ba07fd2fSBen Hoff dec_mode = readl(hws->bar0_base + HWS_REG_DEC_MODE); 166*ba07fd2fSBen Hoff 167*ba07fd2fSBen Hoff dev_dbg(dev, 168*ba07fd2fSBen Hoff "lifecycle:%s:%s suspended=%d start_run=%d pci_lost=%d irq=%d INT_EN=0x%08x INT_STATUS=0x%08x VCAP=0x%08x SYS=0x%08x DEC=0x%08x\n", 169*ba07fd2fSBen Hoff action, phase, READ_ONCE(hws->suspended), hws->start_run, 170*ba07fd2fSBen Hoff hws->pci_lost, hws->irq, int_en, int_status, vcap, 171*ba07fd2fSBen Hoff sys_status, dec_mode); 172*ba07fd2fSBen Hoff } 173*ba07fd2fSBen Hoff 174*ba07fd2fSBen Hoff static int read_chip_id(struct hws_pcie_dev *hdev) 175*ba07fd2fSBen Hoff { 176*ba07fd2fSBen Hoff u32 reg; 177*ba07fd2fSBen Hoff /* mirror PCI IDs for later switches */ 178*ba07fd2fSBen Hoff hdev->device_id = hdev->pdev->device; 179*ba07fd2fSBen Hoff hdev->vendor_id = hdev->pdev->vendor; 180*ba07fd2fSBen Hoff 181*ba07fd2fSBen Hoff reg = readl(hdev->bar0_base + HWS_REG_DEVICE_INFO); 182*ba07fd2fSBen Hoff 183*ba07fd2fSBen Hoff hdev->device_ver = FIELD_GET(DEVINFO_VER, reg); 184*ba07fd2fSBen Hoff hdev->sub_ver = FIELD_GET(DEVINFO_SUBVER, reg); 185*ba07fd2fSBen Hoff hdev->support_yv12 = FIELD_GET(DEVINFO_YV12, reg); 186*ba07fd2fSBen Hoff hdev->port_id = FIELD_GET(DEVINFO_PORTID, reg); 187*ba07fd2fSBen Hoff 188*ba07fd2fSBen Hoff hdev->max_hw_video_buf_sz = MAX_MM_VIDEO_SIZE; 189*ba07fd2fSBen Hoff hdev->max_channels = 4; 190*ba07fd2fSBen Hoff hdev->buf_allocated = false; 191*ba07fd2fSBen Hoff hdev->main_task = NULL; 192*ba07fd2fSBen Hoff hdev->start_run = false; 193*ba07fd2fSBen Hoff hdev->pci_lost = 0; 194*ba07fd2fSBen Hoff 195*ba07fd2fSBen Hoff writel(0x00, hdev->bar0_base + HWS_REG_DEC_MODE); 196*ba07fd2fSBen Hoff writel(0x10, hdev->bar0_base + HWS_REG_DEC_MODE); 197*ba07fd2fSBen Hoff 198*ba07fd2fSBen Hoff hws_configure_hardware_capabilities(hdev); 199*ba07fd2fSBen Hoff 200*ba07fd2fSBen Hoff dev_info(&hdev->pdev->dev, 201*ba07fd2fSBen Hoff "chip detected: ver=%u subver=%u port=%u yv12=%u\n", 202*ba07fd2fSBen Hoff hdev->device_ver, hdev->sub_ver, hdev->port_id, 203*ba07fd2fSBen Hoff hdev->support_yv12); 204*ba07fd2fSBen Hoff 205*ba07fd2fSBen Hoff return 0; 206*ba07fd2fSBen Hoff } 207*ba07fd2fSBen Hoff 208*ba07fd2fSBen Hoff static int main_ks_thread_handle(void *data) 209*ba07fd2fSBen Hoff { 210*ba07fd2fSBen Hoff struct hws_pcie_dev *pdx = data; 211*ba07fd2fSBen Hoff 212*ba07fd2fSBen Hoff set_freezable(); 213*ba07fd2fSBen Hoff 214*ba07fd2fSBen Hoff while (!kthread_should_stop()) { 215*ba07fd2fSBen Hoff /* If we're suspending, don't touch hardware; just sleep/freeze. */ 216*ba07fd2fSBen Hoff if (READ_ONCE(pdx->suspended)) { 217*ba07fd2fSBen Hoff try_to_freeze(); 218*ba07fd2fSBen Hoff schedule_timeout_interruptible(msecs_to_jiffies(1000)); 219*ba07fd2fSBen Hoff continue; 220*ba07fd2fSBen Hoff } 221*ba07fd2fSBen Hoff 222*ba07fd2fSBen Hoff /* avoid MMIO when suspended (guarded above) */ 223*ba07fd2fSBen Hoff check_video_format(pdx); 224*ba07fd2fSBen Hoff 225*ba07fd2fSBen Hoff try_to_freeze(); /* cooperate with freezer each loop */ 226*ba07fd2fSBen Hoff 227*ba07fd2fSBen Hoff /* Sleep 1s or until signaled to wake/stop */ 228*ba07fd2fSBen Hoff schedule_timeout_interruptible(msecs_to_jiffies(1000)); 229*ba07fd2fSBen Hoff } 230*ba07fd2fSBen Hoff 231*ba07fd2fSBen Hoff dev_dbg(&pdx->pdev->dev, "%s: exiting\n", __func__); 232*ba07fd2fSBen Hoff return 0; 233*ba07fd2fSBen Hoff } 234*ba07fd2fSBen Hoff 235*ba07fd2fSBen Hoff static void hws_stop_kthread_action(void *data) 236*ba07fd2fSBen Hoff { 237*ba07fd2fSBen Hoff struct hws_pcie_dev *hws = data; 238*ba07fd2fSBen Hoff struct task_struct *t; 239*ba07fd2fSBen Hoff u64 start_ns; 240*ba07fd2fSBen Hoff 241*ba07fd2fSBen Hoff if (!hws) 242*ba07fd2fSBen Hoff return; 243*ba07fd2fSBen Hoff 244*ba07fd2fSBen Hoff t = READ_ONCE(hws->main_task); 245*ba07fd2fSBen Hoff if (!IS_ERR_OR_NULL(t)) { 246*ba07fd2fSBen Hoff start_ns = ktime_get_mono_fast_ns(); 247*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 248*ba07fd2fSBen Hoff "lifecycle:kthread-stop:begin task=%s[%d]\n", 249*ba07fd2fSBen Hoff t->comm, t->pid); 250*ba07fd2fSBen Hoff WRITE_ONCE(hws->main_task, NULL); 251*ba07fd2fSBen Hoff kthread_stop(t); 252*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 253*ba07fd2fSBen Hoff "lifecycle:kthread-stop:done (%lluus)\n", 254*ba07fd2fSBen Hoff hws_elapsed_us(start_ns)); 255*ba07fd2fSBen Hoff } 256*ba07fd2fSBen Hoff } 257*ba07fd2fSBen Hoff 258*ba07fd2fSBen Hoff static int hws_alloc_seed_buffers(struct hws_pcie_dev *hws) 259*ba07fd2fSBen Hoff { 260*ba07fd2fSBen Hoff int ch; 261*ba07fd2fSBen Hoff /* 64 KiB is plenty for a safe dummy; hardware needs 64-byte alignment. */ 262*ba07fd2fSBen Hoff const size_t need = ALIGN(64 * 1024, 64); 263*ba07fd2fSBen Hoff 264*ba07fd2fSBen Hoff for (ch = 0; ch < hws->cur_max_video_ch; ch++) { 265*ba07fd2fSBen Hoff #if defined(CONFIG_HAS_DMA) /* normal on PCIe platforms */ 266*ba07fd2fSBen Hoff void *cpu = dma_alloc_coherent(&hws->pdev->dev, need, 267*ba07fd2fSBen Hoff &hws->scratch_vid[ch].dma, 268*ba07fd2fSBen Hoff GFP_KERNEL); 269*ba07fd2fSBen Hoff #else 270*ba07fd2fSBen Hoff void *cpu = NULL; 271*ba07fd2fSBen Hoff #endif 272*ba07fd2fSBen Hoff if (!cpu) { 273*ba07fd2fSBen Hoff dev_warn(&hws->pdev->dev, 274*ba07fd2fSBen Hoff "scratch: dma_alloc_coherent failed ch=%d\n", ch); 275*ba07fd2fSBen Hoff /* not fatal: free earlier ones and continue without seeding */ 276*ba07fd2fSBen Hoff while (--ch >= 0) { 277*ba07fd2fSBen Hoff if (hws->scratch_vid[ch].cpu) 278*ba07fd2fSBen Hoff dma_free_coherent(&hws->pdev->dev, 279*ba07fd2fSBen Hoff hws->scratch_vid[ch].size, 280*ba07fd2fSBen Hoff hws->scratch_vid[ch].cpu, 281*ba07fd2fSBen Hoff hws->scratch_vid[ch].dma); 282*ba07fd2fSBen Hoff hws->scratch_vid[ch].cpu = NULL; 283*ba07fd2fSBen Hoff hws->scratch_vid[ch].size = 0; 284*ba07fd2fSBen Hoff } 285*ba07fd2fSBen Hoff return -ENOMEM; 286*ba07fd2fSBen Hoff } 287*ba07fd2fSBen Hoff hws->scratch_vid[ch].cpu = cpu; 288*ba07fd2fSBen Hoff hws->scratch_vid[ch].size = need; 289*ba07fd2fSBen Hoff } 290*ba07fd2fSBen Hoff return 0; 291*ba07fd2fSBen Hoff } 292*ba07fd2fSBen Hoff 293*ba07fd2fSBen Hoff static void hws_free_seed_buffers(struct hws_pcie_dev *hws) 294*ba07fd2fSBen Hoff { 295*ba07fd2fSBen Hoff int ch; 296*ba07fd2fSBen Hoff 297*ba07fd2fSBen Hoff for (ch = 0; ch < hws->cur_max_video_ch; ch++) { 298*ba07fd2fSBen Hoff if (hws->scratch_vid[ch].cpu) { 299*ba07fd2fSBen Hoff dma_free_coherent(&hws->pdev->dev, 300*ba07fd2fSBen Hoff hws->scratch_vid[ch].size, 301*ba07fd2fSBen Hoff hws->scratch_vid[ch].cpu, 302*ba07fd2fSBen Hoff hws->scratch_vid[ch].dma); 303*ba07fd2fSBen Hoff hws->scratch_vid[ch].cpu = NULL; 304*ba07fd2fSBen Hoff hws->scratch_vid[ch].size = 0; 305*ba07fd2fSBen Hoff } 306*ba07fd2fSBen Hoff } 307*ba07fd2fSBen Hoff } 308*ba07fd2fSBen Hoff 309*ba07fd2fSBen Hoff static void hws_seed_channel(struct hws_pcie_dev *hws, int ch) 310*ba07fd2fSBen Hoff { 311*ba07fd2fSBen Hoff dma_addr_t paddr = hws->scratch_vid[ch].dma; 312*ba07fd2fSBen Hoff u32 lo = lower_32_bits(paddr); 313*ba07fd2fSBen Hoff u32 hi = upper_32_bits(paddr); 314*ba07fd2fSBen Hoff u32 pci_addr = lo & PCI_E_BAR_ADD_LOWMASK; 315*ba07fd2fSBen Hoff 316*ba07fd2fSBen Hoff lo &= PCI_E_BAR_ADD_MASK; 317*ba07fd2fSBen Hoff 318*ba07fd2fSBen Hoff /* Program 64-bit BAR remap entry for this channel (table @ 0x208 + ch * 8) */ 319*ba07fd2fSBen Hoff writel_relaxed(hi, hws->bar0_base + 320*ba07fd2fSBen Hoff PCI_ADDR_TABLE_BASE + 0x208 + ch * 8); 321*ba07fd2fSBen Hoff writel_relaxed(lo, hws->bar0_base + 322*ba07fd2fSBen Hoff PCI_ADDR_TABLE_BASE + 0x208 + ch * 8 + 323*ba07fd2fSBen Hoff PCIE_BARADDROFSIZE); 324*ba07fd2fSBen Hoff 325*ba07fd2fSBen Hoff /* Program capture engine per-channel base/half */ 326*ba07fd2fSBen Hoff writel_relaxed((ch + 1) * PCIEBAR_AXI_BASE + pci_addr, 327*ba07fd2fSBen Hoff hws->bar0_base + CVBS_IN_BUF_BASE + 328*ba07fd2fSBen Hoff ch * PCIE_BARADDROFSIZE); 329*ba07fd2fSBen Hoff 330*ba07fd2fSBen Hoff /* Half size: use either the current format's half or half of scratch. */ 331*ba07fd2fSBen Hoff { 332*ba07fd2fSBen Hoff u32 half = hws->video[ch].pix.half_size ? 333*ba07fd2fSBen Hoff hws->video[ch].pix.half_size : 334*ba07fd2fSBen Hoff (u32)(hws->scratch_vid[ch].size / 2); 335*ba07fd2fSBen Hoff 336*ba07fd2fSBen Hoff writel_relaxed(half / 16, 337*ba07fd2fSBen Hoff hws->bar0_base + CVBS_IN_BUF_BASE2 + 338*ba07fd2fSBen Hoff ch * PCIE_BARADDROFSIZE); 339*ba07fd2fSBen Hoff } 340*ba07fd2fSBen Hoff 341*ba07fd2fSBen Hoff (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); /* flush posted writes */ 342*ba07fd2fSBen Hoff } 343*ba07fd2fSBen Hoff 344*ba07fd2fSBen Hoff static void hws_seed_all_channels(struct hws_pcie_dev *hws) 345*ba07fd2fSBen Hoff { 346*ba07fd2fSBen Hoff int ch; 347*ba07fd2fSBen Hoff 348*ba07fd2fSBen Hoff for (ch = 0; ch < hws->cur_max_video_ch; ch++) { 349*ba07fd2fSBen Hoff if (hws->scratch_vid[ch].cpu) 350*ba07fd2fSBen Hoff hws_seed_channel(hws, ch); 351*ba07fd2fSBen Hoff } 352*ba07fd2fSBen Hoff } 353*ba07fd2fSBen Hoff 354*ba07fd2fSBen Hoff static void hws_irq_mask_gate(struct hws_pcie_dev *hws) 355*ba07fd2fSBen Hoff { 356*ba07fd2fSBen Hoff writel(0x00000000, hws->bar0_base + INT_EN_REG_BASE); 357*ba07fd2fSBen Hoff (void)readl(hws->bar0_base + INT_EN_REG_BASE); 358*ba07fd2fSBen Hoff } 359*ba07fd2fSBen Hoff 360*ba07fd2fSBen Hoff static void hws_irq_unmask_gate(struct hws_pcie_dev *hws) 361*ba07fd2fSBen Hoff { 362*ba07fd2fSBen Hoff writel(HWS_INT_EN_MASK, hws->bar0_base + INT_EN_REG_BASE); 363*ba07fd2fSBen Hoff (void)readl(hws->bar0_base + INT_EN_REG_BASE); 364*ba07fd2fSBen Hoff } 365*ba07fd2fSBen Hoff 366*ba07fd2fSBen Hoff static void hws_irq_clear_pending(struct hws_pcie_dev *hws) 367*ba07fd2fSBen Hoff { 368*ba07fd2fSBen Hoff u32 st = readl(hws->bar0_base + HWS_REG_INT_STATUS); 369*ba07fd2fSBen Hoff 370*ba07fd2fSBen Hoff if (st) { 371*ba07fd2fSBen Hoff writel(st, hws->bar0_base + HWS_REG_INT_STATUS); /* W1C */ 372*ba07fd2fSBen Hoff (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); 373*ba07fd2fSBen Hoff } 374*ba07fd2fSBen Hoff } 375*ba07fd2fSBen Hoff 376*ba07fd2fSBen Hoff static void hws_block_hotpaths(struct hws_pcie_dev *hws) 377*ba07fd2fSBen Hoff { 378*ba07fd2fSBen Hoff WRITE_ONCE(hws->suspended, true); 379*ba07fd2fSBen Hoff if (hws->irq >= 0) 380*ba07fd2fSBen Hoff disable_irq(hws->irq); 381*ba07fd2fSBen Hoff 382*ba07fd2fSBen Hoff if (!hws->bar0_base) 383*ba07fd2fSBen Hoff return; 384*ba07fd2fSBen Hoff 385*ba07fd2fSBen Hoff hws_irq_mask_gate(hws); 386*ba07fd2fSBen Hoff hws_irq_clear_pending(hws); 387*ba07fd2fSBen Hoff } 388*ba07fd2fSBen Hoff 389*ba07fd2fSBen Hoff static int hws_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) 390*ba07fd2fSBen Hoff { 391*ba07fd2fSBen Hoff struct hws_pcie_dev *hws; 392*ba07fd2fSBen Hoff int i, ret, irq; 393*ba07fd2fSBen Hoff unsigned long irqf = 0; 394*ba07fd2fSBen Hoff bool v4l2_registered = false; 395*ba07fd2fSBen Hoff 396*ba07fd2fSBen Hoff /* devres-backed device object */ 397*ba07fd2fSBen Hoff hws = devm_kzalloc(&pdev->dev, sizeof(*hws), GFP_KERNEL); 398*ba07fd2fSBen Hoff if (!hws) 399*ba07fd2fSBen Hoff return -ENOMEM; 400*ba07fd2fSBen Hoff 401*ba07fd2fSBen Hoff hws->pdev = pdev; 402*ba07fd2fSBen Hoff hws->irq = -1; 403*ba07fd2fSBen Hoff hws->suspended = false; 404*ba07fd2fSBen Hoff pci_set_drvdata(pdev, hws); 405*ba07fd2fSBen Hoff 406*ba07fd2fSBen Hoff /* 1) Enable device + bus mastering (managed) */ 407*ba07fd2fSBen Hoff ret = pcim_enable_device(pdev); 408*ba07fd2fSBen Hoff if (ret) 409*ba07fd2fSBen Hoff return dev_err_probe(&pdev->dev, ret, "pcim_enable_device\n"); 410*ba07fd2fSBen Hoff pci_set_master(pdev); 411*ba07fd2fSBen Hoff 412*ba07fd2fSBen Hoff /* 2) Map BAR0 (managed) */ 413*ba07fd2fSBen Hoff ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); 414*ba07fd2fSBen Hoff if (ret) 415*ba07fd2fSBen Hoff return dev_err_probe(&pdev->dev, ret, "pcim_iomap_regions BAR0\n"); 416*ba07fd2fSBen Hoff hws->bar0_base = pcim_iomap_table(pdev)[0]; 417*ba07fd2fSBen Hoff 418*ba07fd2fSBen Hoff ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 419*ba07fd2fSBen Hoff if (ret) { 420*ba07fd2fSBen Hoff dev_warn(&pdev->dev, 421*ba07fd2fSBen Hoff "64-bit DMA mask unavailable, falling back to 32-bit (%d)\n", 422*ba07fd2fSBen Hoff ret); 423*ba07fd2fSBen Hoff ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 424*ba07fd2fSBen Hoff if (ret) 425*ba07fd2fSBen Hoff return dev_err_probe(&pdev->dev, ret, 426*ba07fd2fSBen Hoff "No suitable DMA configuration\n"); 427*ba07fd2fSBen Hoff } else { 428*ba07fd2fSBen Hoff dev_dbg(&pdev->dev, "Using 64-bit DMA mask\n"); 429*ba07fd2fSBen Hoff } 430*ba07fd2fSBen Hoff 431*ba07fd2fSBen Hoff /* 3) Apply optional PCIe tuning. */ 432*ba07fd2fSBen Hoff enable_pcie_relaxed_ordering(pdev); 433*ba07fd2fSBen Hoff #ifdef CONFIG_ARCH_TI816X 434*ba07fd2fSBen Hoff pcie_set_readrq(pdev, 128); 435*ba07fd2fSBen Hoff #endif 436*ba07fd2fSBen Hoff 437*ba07fd2fSBen Hoff /* 4) Identify chip & capabilities */ 438*ba07fd2fSBen Hoff read_chip_id(hws); 439*ba07fd2fSBen Hoff dev_info(&pdev->dev, "Device VID=0x%04x DID=0x%04x\n", 440*ba07fd2fSBen Hoff pdev->vendor, pdev->device); 441*ba07fd2fSBen Hoff hws_init_video_sys(hws, false); 442*ba07fd2fSBen Hoff 443*ba07fd2fSBen Hoff /* 5) Init channels (video state, locks, vb2, ctrls) */ 444*ba07fd2fSBen Hoff for (i = 0; i < hws->max_channels; i++) { 445*ba07fd2fSBen Hoff ret = hws_video_init_channel(hws, i); 446*ba07fd2fSBen Hoff if (ret) { 447*ba07fd2fSBen Hoff dev_err(&pdev->dev, "video channel init failed (ch=%d)\n", i); 448*ba07fd2fSBen Hoff goto err_unwind_channels; 449*ba07fd2fSBen Hoff } 450*ba07fd2fSBen Hoff } 451*ba07fd2fSBen Hoff 452*ba07fd2fSBen Hoff /* 6) Allocate scratch DMA and seed BAR table + channel base/half (legacy SetDMAAddress) */ 453*ba07fd2fSBen Hoff ret = hws_alloc_seed_buffers(hws); 454*ba07fd2fSBen Hoff if (!ret) 455*ba07fd2fSBen Hoff hws_seed_all_channels(hws); 456*ba07fd2fSBen Hoff 457*ba07fd2fSBen Hoff /* 7) Start-run sequence. */ 458*ba07fd2fSBen Hoff hws_init_video_sys(hws, false); 459*ba07fd2fSBen Hoff 460*ba07fd2fSBen Hoff /* A) Force legacy INTx; legacy used request_irq(pdev->irq, ..., IRQF_SHARED) */ 461*ba07fd2fSBen Hoff pci_intx(pdev, 1); 462*ba07fd2fSBen Hoff irqf = IRQF_SHARED; 463*ba07fd2fSBen Hoff irq = pdev->irq; 464*ba07fd2fSBen Hoff hws->irq = irq; 465*ba07fd2fSBen Hoff dev_info(&pdev->dev, "IRQ mode: legacy INTx (shared), irq=%d\n", irq); 466*ba07fd2fSBen Hoff 467*ba07fd2fSBen Hoff /* B) Mask the device's global/bridge gate (INT_EN_REG_BASE) */ 468*ba07fd2fSBen Hoff hws_irq_mask_gate(hws); 469*ba07fd2fSBen Hoff 470*ba07fd2fSBen Hoff /* C) Clear any sticky pending interrupt status (W1C) before we arm the line */ 471*ba07fd2fSBen Hoff hws_irq_clear_pending(hws); 472*ba07fd2fSBen Hoff 473*ba07fd2fSBen Hoff /* D) Request the legacy shared interrupt line (no vectors/MSI/MSI-X) */ 474*ba07fd2fSBen Hoff ret = devm_request_irq(&pdev->dev, irq, hws_irq_handler, irqf, 475*ba07fd2fSBen Hoff dev_name(&pdev->dev), hws); 476*ba07fd2fSBen Hoff if (ret) { 477*ba07fd2fSBen Hoff dev_err(&pdev->dev, "request_irq(%d) failed: %d\n", irq, ret); 478*ba07fd2fSBen Hoff goto err_unwind_channels; 479*ba07fd2fSBen Hoff } 480*ba07fd2fSBen Hoff 481*ba07fd2fSBen Hoff /* E) Set the global interrupt enable bit in main control register */ 482*ba07fd2fSBen Hoff { 483*ba07fd2fSBen Hoff u32 ctl_reg = readl(hws->bar0_base + HWS_REG_CTL); 484*ba07fd2fSBen Hoff 485*ba07fd2fSBen Hoff ctl_reg |= HWS_CTL_IRQ_ENABLE_BIT; 486*ba07fd2fSBen Hoff writel(ctl_reg, hws->bar0_base + HWS_REG_CTL); 487*ba07fd2fSBen Hoff (void)readl(hws->bar0_base + HWS_REG_CTL); /* flush write */ 488*ba07fd2fSBen Hoff dev_info(&pdev->dev, "Global IRQ enable bit set in control register\n"); 489*ba07fd2fSBen Hoff } 490*ba07fd2fSBen Hoff 491*ba07fd2fSBen Hoff /* F) Open the global gate just like legacy did */ 492*ba07fd2fSBen Hoff hws_irq_unmask_gate(hws); 493*ba07fd2fSBen Hoff dev_info(&pdev->dev, "INT_EN_GATE readback=0x%08x\n", 494*ba07fd2fSBen Hoff readl(hws->bar0_base + INT_EN_REG_BASE)); 495*ba07fd2fSBen Hoff 496*ba07fd2fSBen Hoff /* 11) Register V4L2 */ 497*ba07fd2fSBen Hoff ret = hws_video_register(hws); 498*ba07fd2fSBen Hoff if (ret) { 499*ba07fd2fSBen Hoff dev_err(&pdev->dev, "video_register: %d\n", ret); 500*ba07fd2fSBen Hoff goto err_unwind_channels; 501*ba07fd2fSBen Hoff } 502*ba07fd2fSBen Hoff v4l2_registered = true; 503*ba07fd2fSBen Hoff 504*ba07fd2fSBen Hoff /* 12) Background monitor thread (managed) */ 505*ba07fd2fSBen Hoff hws->main_task = kthread_run(main_ks_thread_handle, hws, "hws-mon"); 506*ba07fd2fSBen Hoff if (IS_ERR(hws->main_task)) { 507*ba07fd2fSBen Hoff ret = PTR_ERR(hws->main_task); 508*ba07fd2fSBen Hoff hws->main_task = NULL; 509*ba07fd2fSBen Hoff dev_err(&pdev->dev, "kthread_run: %d\n", ret); 510*ba07fd2fSBen Hoff goto err_unregister_va; 511*ba07fd2fSBen Hoff } 512*ba07fd2fSBen Hoff ret = devm_add_action_or_reset(&pdev->dev, hws_stop_kthread_action, hws); 513*ba07fd2fSBen Hoff if (ret) { 514*ba07fd2fSBen Hoff dev_err(&pdev->dev, "devm_add_action kthread_stop: %d\n", ret); 515*ba07fd2fSBen Hoff goto err_unregister_va; /* reset already stopped the thread */ 516*ba07fd2fSBen Hoff } 517*ba07fd2fSBen Hoff 518*ba07fd2fSBen Hoff /* 13) Final: show the line is armed */ 519*ba07fd2fSBen Hoff dev_info(&pdev->dev, "irq handler installed on irq=%d\n", irq); 520*ba07fd2fSBen Hoff return 0; 521*ba07fd2fSBen Hoff 522*ba07fd2fSBen Hoff err_unregister_va: 523*ba07fd2fSBen Hoff hws_stop_device(hws); 524*ba07fd2fSBen Hoff hws_video_unregister(hws); 525*ba07fd2fSBen Hoff hws_free_seed_buffers(hws); 526*ba07fd2fSBen Hoff return ret; 527*ba07fd2fSBen Hoff err_unwind_channels: 528*ba07fd2fSBen Hoff hws_free_seed_buffers(hws); 529*ba07fd2fSBen Hoff if (!v4l2_registered) { 530*ba07fd2fSBen Hoff while (--i >= 0) 531*ba07fd2fSBen Hoff hws_video_cleanup_channel(hws, i); 532*ba07fd2fSBen Hoff } 533*ba07fd2fSBen Hoff return ret; 534*ba07fd2fSBen Hoff } 535*ba07fd2fSBen Hoff 536*ba07fd2fSBen Hoff static int hws_check_busy(struct hws_pcie_dev *pdx) 537*ba07fd2fSBen Hoff { 538*ba07fd2fSBen Hoff void __iomem *reg = pdx->bar0_base + HWS_REG_SYS_STATUS; 539*ba07fd2fSBen Hoff u32 val; 540*ba07fd2fSBen Hoff int ret; 541*ba07fd2fSBen Hoff 542*ba07fd2fSBen Hoff /* poll until !(val & BUSY_BIT), sleeping HWS_BUSY_POLL_DELAY_US between reads */ 543*ba07fd2fSBen Hoff ret = readl_poll_timeout(reg, val, !(val & HWS_SYS_DMA_BUSY_BIT), 544*ba07fd2fSBen Hoff HWS_BUSY_POLL_DELAY_US, 545*ba07fd2fSBen Hoff HWS_BUSY_POLL_TIMEOUT_US); 546*ba07fd2fSBen Hoff if (ret) { 547*ba07fd2fSBen Hoff dev_err(&pdx->pdev->dev, 548*ba07fd2fSBen Hoff "SYS_STATUS busy bit never cleared (0x%08x)\n", val); 549*ba07fd2fSBen Hoff return -ETIMEDOUT; 550*ba07fd2fSBen Hoff } 551*ba07fd2fSBen Hoff 552*ba07fd2fSBen Hoff return 0; 553*ba07fd2fSBen Hoff } 554*ba07fd2fSBen Hoff 555*ba07fd2fSBen Hoff static void hws_stop_dsp(struct hws_pcie_dev *hws) 556*ba07fd2fSBen Hoff { 557*ba07fd2fSBen Hoff u32 status; 558*ba07fd2fSBen Hoff 559*ba07fd2fSBen Hoff /* Read the decoder mode/status register */ 560*ba07fd2fSBen Hoff status = readl(hws->bar0_base + HWS_REG_DEC_MODE); 561*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, "%s: status=0x%08x\n", __func__, status); 562*ba07fd2fSBen Hoff 563*ba07fd2fSBen Hoff /* If the device looks unplugged/stuck, bail out */ 564*ba07fd2fSBen Hoff if (status == 0xFFFFFFFF) 565*ba07fd2fSBen Hoff return; 566*ba07fd2fSBen Hoff 567*ba07fd2fSBen Hoff /* Tell the DSP to stop */ 568*ba07fd2fSBen Hoff writel(0x10, hws->bar0_base + HWS_REG_DEC_MODE); 569*ba07fd2fSBen Hoff 570*ba07fd2fSBen Hoff if (hws_check_busy(hws)) 571*ba07fd2fSBen Hoff dev_warn(&hws->pdev->dev, "DSP busy timeout on stop\n"); 572*ba07fd2fSBen Hoff /* Disable video capture engine in the DSP */ 573*ba07fd2fSBen Hoff writel(0x0, hws->bar0_base + HWS_REG_VCAP_ENABLE); 574*ba07fd2fSBen Hoff } 575*ba07fd2fSBen Hoff 576*ba07fd2fSBen Hoff /* Publish stop so ISR/BH will not touch video buffers anymore. */ 577*ba07fd2fSBen Hoff static void hws_publish_stop_flags(struct hws_pcie_dev *hws) 578*ba07fd2fSBen Hoff { 579*ba07fd2fSBen Hoff unsigned int i; 580*ba07fd2fSBen Hoff 581*ba07fd2fSBen Hoff for (i = 0; i < hws->cur_max_video_ch; ++i) { 582*ba07fd2fSBen Hoff struct hws_video *v = &hws->video[i]; 583*ba07fd2fSBen Hoff 584*ba07fd2fSBen Hoff WRITE_ONCE(v->cap_active, false); 585*ba07fd2fSBen Hoff WRITE_ONCE(v->stop_requested, true); 586*ba07fd2fSBen Hoff } 587*ba07fd2fSBen Hoff 588*ba07fd2fSBen Hoff smp_wmb(); /* make flags visible before we touch MMIO/queues */ 589*ba07fd2fSBen Hoff } 590*ba07fd2fSBen Hoff 591*ba07fd2fSBen Hoff /* Drain engines + ISR/BH after flags are published. */ 592*ba07fd2fSBen Hoff static void hws_drain_after_stop(struct hws_pcie_dev *hws) 593*ba07fd2fSBen Hoff { 594*ba07fd2fSBen Hoff u32 ackmask = 0; 595*ba07fd2fSBen Hoff unsigned int i; 596*ba07fd2fSBen Hoff u64 start_ns = ktime_get_mono_fast_ns(); 597*ba07fd2fSBen Hoff 598*ba07fd2fSBen Hoff /* Mask device enables: no new DMA starts. */ 599*ba07fd2fSBen Hoff writel(0x0, hws->bar0_base + HWS_REG_VCAP_ENABLE); 600*ba07fd2fSBen Hoff (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); /* flush */ 601*ba07fd2fSBen Hoff 602*ba07fd2fSBen Hoff /* Let any in-flight DMAs finish (best-effort). */ 603*ba07fd2fSBen Hoff (void)hws_check_busy(hws); 604*ba07fd2fSBen Hoff 605*ba07fd2fSBen Hoff /* Ack any latched VDONE. */ 606*ba07fd2fSBen Hoff for (i = 0; i < hws->cur_max_video_ch; ++i) 607*ba07fd2fSBen Hoff ackmask |= HWS_INT_VDONE_BIT(i); 608*ba07fd2fSBen Hoff if (ackmask) { 609*ba07fd2fSBen Hoff writel(ackmask, hws->bar0_base + HWS_REG_INT_STATUS); 610*ba07fd2fSBen Hoff (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); 611*ba07fd2fSBen Hoff } 612*ba07fd2fSBen Hoff 613*ba07fd2fSBen Hoff /* Ensure no hard IRQ is still running. */ 614*ba07fd2fSBen Hoff if (hws->irq >= 0) 615*ba07fd2fSBen Hoff synchronize_irq(hws->irq); 616*ba07fd2fSBen Hoff 617*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, "lifecycle:drain-after-stop:done (%lluus)\n", 618*ba07fd2fSBen Hoff hws_elapsed_us(start_ns)); 619*ba07fd2fSBen Hoff } 620*ba07fd2fSBen Hoff 621*ba07fd2fSBen Hoff static void hws_stop_device(struct hws_pcie_dev *hws) 622*ba07fd2fSBen Hoff { 623*ba07fd2fSBen Hoff u32 status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); 624*ba07fd2fSBen Hoff u64 start_ns = ktime_get_mono_fast_ns(); 625*ba07fd2fSBen Hoff bool live = status != 0xFFFFFFFF; 626*ba07fd2fSBen Hoff 627*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, "%s: status=0x%08x\n", __func__, status); 628*ba07fd2fSBen Hoff if (!live) { 629*ba07fd2fSBen Hoff hws->pci_lost = true; 630*ba07fd2fSBen Hoff goto out; 631*ba07fd2fSBen Hoff } 632*ba07fd2fSBen Hoff hws_log_lifecycle_snapshot(hws, "stop-device", "begin"); 633*ba07fd2fSBen Hoff 634*ba07fd2fSBen Hoff /* Make ISR/BH a no-op, then drain engines/IRQ. */ 635*ba07fd2fSBen Hoff hws_publish_stop_flags(hws); 636*ba07fd2fSBen Hoff hws_drain_after_stop(hws); 637*ba07fd2fSBen Hoff 638*ba07fd2fSBen Hoff /* 1) Stop the on-board DSP */ 639*ba07fd2fSBen Hoff hws_stop_dsp(hws); 640*ba07fd2fSBen Hoff 641*ba07fd2fSBen Hoff out: 642*ba07fd2fSBen Hoff hws->start_run = false; 643*ba07fd2fSBen Hoff if (live) 644*ba07fd2fSBen Hoff hws_log_lifecycle_snapshot(hws, "stop-device", "end"); 645*ba07fd2fSBen Hoff else 646*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, "lifecycle:stop-device:device-lost\n"); 647*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, "lifecycle:stop-device:done (%lluus)\n", 648*ba07fd2fSBen Hoff hws_elapsed_us(start_ns)); 649*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, "%s: complete\n", __func__); 650*ba07fd2fSBen Hoff } 651*ba07fd2fSBen Hoff 652*ba07fd2fSBen Hoff static int hws_quiesce_for_transition(struct hws_pcie_dev *hws, 653*ba07fd2fSBen Hoff const char *action, 654*ba07fd2fSBen Hoff bool stop_thread) 655*ba07fd2fSBen Hoff { 656*ba07fd2fSBen Hoff struct device *dev = &hws->pdev->dev; 657*ba07fd2fSBen Hoff u64 start_ns = ktime_get_mono_fast_ns(); 658*ba07fd2fSBen Hoff u64 step_ns; 659*ba07fd2fSBen Hoff int vret; 660*ba07fd2fSBen Hoff 661*ba07fd2fSBen Hoff hws_log_lifecycle_snapshot(hws, action, "begin"); 662*ba07fd2fSBen Hoff 663*ba07fd2fSBen Hoff step_ns = ktime_get_mono_fast_ns(); 664*ba07fd2fSBen Hoff hws_block_hotpaths(hws); 665*ba07fd2fSBen Hoff dev_dbg(dev, "lifecycle:%s:block-hotpaths (%lluus)\n", action, 666*ba07fd2fSBen Hoff hws_elapsed_us(step_ns)); 667*ba07fd2fSBen Hoff hws_log_lifecycle_snapshot(hws, action, "blocked"); 668*ba07fd2fSBen Hoff 669*ba07fd2fSBen Hoff if (stop_thread) { 670*ba07fd2fSBen Hoff step_ns = ktime_get_mono_fast_ns(); 671*ba07fd2fSBen Hoff hws_stop_kthread_action(hws); 672*ba07fd2fSBen Hoff dev_dbg(dev, "lifecycle:%s:stop-kthread (%lluus)\n", action, 673*ba07fd2fSBen Hoff hws_elapsed_us(step_ns)); 674*ba07fd2fSBen Hoff } 675*ba07fd2fSBen Hoff 676*ba07fd2fSBen Hoff step_ns = ktime_get_mono_fast_ns(); 677*ba07fd2fSBen Hoff vret = hws_video_quiesce(hws, action); 678*ba07fd2fSBen Hoff dev_dbg(dev, "lifecycle:%s:video-quiesce ret=%d (%lluus)\n", action, 679*ba07fd2fSBen Hoff vret, hws_elapsed_us(step_ns)); 680*ba07fd2fSBen Hoff if (vret) 681*ba07fd2fSBen Hoff dev_warn(dev, "lifecycle:%s video quiesce returned %d\n", 682*ba07fd2fSBen Hoff action, vret); 683*ba07fd2fSBen Hoff 684*ba07fd2fSBen Hoff step_ns = ktime_get_mono_fast_ns(); 685*ba07fd2fSBen Hoff hws_stop_device(hws); 686*ba07fd2fSBen Hoff dev_dbg(dev, "lifecycle:%s:stop-device (%lluus)\n", action, 687*ba07fd2fSBen Hoff hws_elapsed_us(step_ns)); 688*ba07fd2fSBen Hoff hws_log_lifecycle_snapshot(hws, action, "end"); 689*ba07fd2fSBen Hoff dev_dbg(dev, "lifecycle:%s:quiesce-done ret=%d (%lluus)\n", action, 690*ba07fd2fSBen Hoff vret, hws_elapsed_us(start_ns)); 691*ba07fd2fSBen Hoff 692*ba07fd2fSBen Hoff return vret; 693*ba07fd2fSBen Hoff } 694*ba07fd2fSBen Hoff 695*ba07fd2fSBen Hoff static void hws_remove(struct pci_dev *pdev) 696*ba07fd2fSBen Hoff { 697*ba07fd2fSBen Hoff struct hws_pcie_dev *hws = pci_get_drvdata(pdev); 698*ba07fd2fSBen Hoff u64 start_ns; 699*ba07fd2fSBen Hoff 700*ba07fd2fSBen Hoff if (!hws) 701*ba07fd2fSBen Hoff return; 702*ba07fd2fSBen Hoff 703*ba07fd2fSBen Hoff start_ns = ktime_get_mono_fast_ns(); 704*ba07fd2fSBen Hoff dev_info(&pdev->dev, "lifecycle:remove begin\n"); 705*ba07fd2fSBen Hoff hws_log_lifecycle_snapshot(hws, "remove", "begin"); 706*ba07fd2fSBen Hoff 707*ba07fd2fSBen Hoff /* Stop the monitor thread before tearing down V4L2/vb2 objects. */ 708*ba07fd2fSBen Hoff hws_block_hotpaths(hws); 709*ba07fd2fSBen Hoff hws_stop_kthread_action(hws); 710*ba07fd2fSBen Hoff 711*ba07fd2fSBen Hoff /* Stop hardware and capture cleanly. */ 712*ba07fd2fSBen Hoff hws_stop_device(hws); 713*ba07fd2fSBen Hoff 714*ba07fd2fSBen Hoff /* Unregister V4L2 resources. */ 715*ba07fd2fSBen Hoff hws_video_unregister(hws); 716*ba07fd2fSBen Hoff 717*ba07fd2fSBen Hoff /* Release seeded DMA buffers */ 718*ba07fd2fSBen Hoff hws_free_seed_buffers(hws); 719*ba07fd2fSBen Hoff /* kthread is stopped by the devm action registered in probe. */ 720*ba07fd2fSBen Hoff hws_log_lifecycle_snapshot(hws, "remove", "end"); 721*ba07fd2fSBen Hoff dev_info(&pdev->dev, "lifecycle:remove done (%lluus)\n", 722*ba07fd2fSBen Hoff hws_elapsed_us(start_ns)); 723*ba07fd2fSBen Hoff } 724*ba07fd2fSBen Hoff 725*ba07fd2fSBen Hoff #ifdef CONFIG_PM_SLEEP 726*ba07fd2fSBen Hoff static int hws_pm_suspend(struct device *dev) 727*ba07fd2fSBen Hoff { 728*ba07fd2fSBen Hoff struct pci_dev *pdev = to_pci_dev(dev); 729*ba07fd2fSBen Hoff struct hws_pcie_dev *hws = pci_get_drvdata(pdev); 730*ba07fd2fSBen Hoff int vret; 731*ba07fd2fSBen Hoff u64 start_ns = ktime_get_mono_fast_ns(); 732*ba07fd2fSBen Hoff u64 step_ns; 733*ba07fd2fSBen Hoff 734*ba07fd2fSBen Hoff dev_info(dev, "lifecycle:pm_suspend begin\n"); 735*ba07fd2fSBen Hoff vret = hws_quiesce_for_transition(hws, "pm_suspend", false); 736*ba07fd2fSBen Hoff 737*ba07fd2fSBen Hoff step_ns = ktime_get_mono_fast_ns(); 738*ba07fd2fSBen Hoff pci_save_state(pdev); 739*ba07fd2fSBen Hoff pci_clear_master(pdev); 740*ba07fd2fSBen Hoff pci_disable_device(pdev); 741*ba07fd2fSBen Hoff pci_set_power_state(pdev, PCI_D3hot); 742*ba07fd2fSBen Hoff dev_dbg(dev, "lifecycle:pm_suspend:pci-d3hot (%lluus)\n", 743*ba07fd2fSBen Hoff hws_elapsed_us(step_ns)); 744*ba07fd2fSBen Hoff dev_info(dev, "lifecycle:pm_suspend done ret=%d (%lluus)\n", vret, 745*ba07fd2fSBen Hoff hws_elapsed_us(start_ns)); 746*ba07fd2fSBen Hoff 747*ba07fd2fSBen Hoff return 0; 748*ba07fd2fSBen Hoff } 749*ba07fd2fSBen Hoff 750*ba07fd2fSBen Hoff static int hws_pm_resume(struct device *dev) 751*ba07fd2fSBen Hoff { 752*ba07fd2fSBen Hoff struct pci_dev *pdev = to_pci_dev(dev); 753*ba07fd2fSBen Hoff struct hws_pcie_dev *hws = pci_get_drvdata(pdev); 754*ba07fd2fSBen Hoff int ret; 755*ba07fd2fSBen Hoff u64 start_ns = ktime_get_mono_fast_ns(); 756*ba07fd2fSBen Hoff u64 step_ns; 757*ba07fd2fSBen Hoff 758*ba07fd2fSBen Hoff dev_info(dev, "lifecycle:pm_resume begin\n"); 759*ba07fd2fSBen Hoff 760*ba07fd2fSBen Hoff /* Back to D0 and re-enable the function */ 761*ba07fd2fSBen Hoff step_ns = ktime_get_mono_fast_ns(); 762*ba07fd2fSBen Hoff pci_set_power_state(pdev, PCI_D0); 763*ba07fd2fSBen Hoff 764*ba07fd2fSBen Hoff ret = pci_enable_device(pdev); 765*ba07fd2fSBen Hoff if (ret) { 766*ba07fd2fSBen Hoff dev_err(dev, "pci_enable_device: %d\n", ret); 767*ba07fd2fSBen Hoff return ret; 768*ba07fd2fSBen Hoff } 769*ba07fd2fSBen Hoff pci_restore_state(pdev); 770*ba07fd2fSBen Hoff pci_set_master(pdev); 771*ba07fd2fSBen Hoff dev_dbg(dev, "lifecycle:pm_resume:pci-enable (%lluus)\n", 772*ba07fd2fSBen Hoff hws_elapsed_us(step_ns)); 773*ba07fd2fSBen Hoff 774*ba07fd2fSBen Hoff /* Reapply any PCIe tuning lost across D3 */ 775*ba07fd2fSBen Hoff enable_pcie_relaxed_ordering(pdev); 776*ba07fd2fSBen Hoff 777*ba07fd2fSBen Hoff /* Reinitialize chip-side capabilities / registers */ 778*ba07fd2fSBen Hoff step_ns = ktime_get_mono_fast_ns(); 779*ba07fd2fSBen Hoff read_chip_id(hws); 780*ba07fd2fSBen Hoff /* Re-seed BAR remaps/DMA windows and restart the capture core */ 781*ba07fd2fSBen Hoff hws_seed_all_channels(hws); 782*ba07fd2fSBen Hoff hws_init_video_sys(hws, true); 783*ba07fd2fSBen Hoff hws_irq_clear_pending(hws); 784*ba07fd2fSBen Hoff dev_dbg(dev, "lifecycle:pm_resume:chip-reinit (%lluus)\n", 785*ba07fd2fSBen Hoff hws_elapsed_us(step_ns)); 786*ba07fd2fSBen Hoff 787*ba07fd2fSBen Hoff /* IRQs can be re-enabled now that MMIO is sane */ 788*ba07fd2fSBen Hoff step_ns = ktime_get_mono_fast_ns(); 789*ba07fd2fSBen Hoff if (hws->irq >= 0) 790*ba07fd2fSBen Hoff enable_irq(hws->irq); 791*ba07fd2fSBen Hoff 792*ba07fd2fSBen Hoff WRITE_ONCE(hws->suspended, false); 793*ba07fd2fSBen Hoff dev_dbg(dev, "lifecycle:pm_resume:irq-unsuspend (%lluus)\n", 794*ba07fd2fSBen Hoff hws_elapsed_us(step_ns)); 795*ba07fd2fSBen Hoff 796*ba07fd2fSBen Hoff /* vb2: nothing mandatory; userspace will STREAMON again when ready */ 797*ba07fd2fSBen Hoff step_ns = ktime_get_mono_fast_ns(); 798*ba07fd2fSBen Hoff hws_video_pm_resume(hws); 799*ba07fd2fSBen Hoff dev_dbg(dev, "lifecycle:pm_resume:video-resume (%lluus)\n", 800*ba07fd2fSBen Hoff hws_elapsed_us(step_ns)); 801*ba07fd2fSBen Hoff hws_log_lifecycle_snapshot(hws, "pm_resume", "end"); 802*ba07fd2fSBen Hoff dev_info(dev, "lifecycle:pm_resume done (%lluus)\n", 803*ba07fd2fSBen Hoff hws_elapsed_us(start_ns)); 804*ba07fd2fSBen Hoff 805*ba07fd2fSBen Hoff return 0; 806*ba07fd2fSBen Hoff } 807*ba07fd2fSBen Hoff 808*ba07fd2fSBen Hoff static SIMPLE_DEV_PM_OPS(hws_pm_ops, hws_pm_suspend, hws_pm_resume); 809*ba07fd2fSBen Hoff # define HWS_PM_OPS (&hws_pm_ops) 810*ba07fd2fSBen Hoff #else 811*ba07fd2fSBen Hoff # define HWS_PM_OPS NULL 812*ba07fd2fSBen Hoff #endif 813*ba07fd2fSBen Hoff 814*ba07fd2fSBen Hoff static void hws_shutdown(struct pci_dev *pdev) 815*ba07fd2fSBen Hoff { 816*ba07fd2fSBen Hoff struct hws_pcie_dev *hws = pci_get_drvdata(pdev); 817*ba07fd2fSBen Hoff int vret = 0; 818*ba07fd2fSBen Hoff u64 start_ns = ktime_get_mono_fast_ns(); 819*ba07fd2fSBen Hoff u64 step_ns; 820*ba07fd2fSBen Hoff 821*ba07fd2fSBen Hoff if (!hws) 822*ba07fd2fSBen Hoff return; 823*ba07fd2fSBen Hoff 824*ba07fd2fSBen Hoff dev_info(&pdev->dev, "lifecycle:pci_shutdown begin\n"); 825*ba07fd2fSBen Hoff vret = hws_quiesce_for_transition(hws, "pci_shutdown", true); 826*ba07fd2fSBen Hoff 827*ba07fd2fSBen Hoff step_ns = ktime_get_mono_fast_ns(); 828*ba07fd2fSBen Hoff pci_clear_master(pdev); 829*ba07fd2fSBen Hoff dev_dbg(&pdev->dev, "lifecycle:pci_shutdown:clear-master (%lluus)\n", 830*ba07fd2fSBen Hoff hws_elapsed_us(step_ns)); 831*ba07fd2fSBen Hoff dev_info(&pdev->dev, "lifecycle:pci_shutdown done ret=%d (%lluus)\n", 832*ba07fd2fSBen Hoff vret, hws_elapsed_us(start_ns)); 833*ba07fd2fSBen Hoff } 834*ba07fd2fSBen Hoff 835*ba07fd2fSBen Hoff static struct pci_driver hws_pci_driver = { 836*ba07fd2fSBen Hoff .name = KBUILD_MODNAME, 837*ba07fd2fSBen Hoff .id_table = hws_pci_table, 838*ba07fd2fSBen Hoff .probe = hws_probe, 839*ba07fd2fSBen Hoff .remove = hws_remove, 840*ba07fd2fSBen Hoff .shutdown = hws_shutdown, 841*ba07fd2fSBen Hoff .driver = { 842*ba07fd2fSBen Hoff .pm = HWS_PM_OPS, 843*ba07fd2fSBen Hoff }, 844*ba07fd2fSBen Hoff }; 845*ba07fd2fSBen Hoff 846*ba07fd2fSBen Hoff MODULE_DEVICE_TABLE(pci, hws_pci_table); 847*ba07fd2fSBen Hoff 848*ba07fd2fSBen Hoff static int __init pcie_hws_init(void) 849*ba07fd2fSBen Hoff { 850*ba07fd2fSBen Hoff return pci_register_driver(&hws_pci_driver); 851*ba07fd2fSBen Hoff } 852*ba07fd2fSBen Hoff 853*ba07fd2fSBen Hoff static void __exit pcie_hws_exit(void) 854*ba07fd2fSBen Hoff { 855*ba07fd2fSBen Hoff pci_unregister_driver(&hws_pci_driver); 856*ba07fd2fSBen Hoff } 857*ba07fd2fSBen Hoff 858*ba07fd2fSBen Hoff module_init(pcie_hws_init); 859*ba07fd2fSBen Hoff module_exit(pcie_hws_exit); 860*ba07fd2fSBen Hoff 861*ba07fd2fSBen Hoff MODULE_DESCRIPTION(DRV_NAME); 862*ba07fd2fSBen Hoff MODULE_AUTHOR("Ben Hoff <hoff.benjamin.k@gmail.com>"); 863*ba07fd2fSBen Hoff MODULE_AUTHOR("Sales <sales@avmatrix.com>"); 864*ba07fd2fSBen Hoff MODULE_LICENSE("GPL"); 865*ba07fd2fSBen Hoff MODULE_IMPORT_NS("DMA_BUF"); 866