xref: /linux/drivers/media/pci/hws/hws_pci.c (revision 3d5e48944e824bddc20d7b874e784f7b279636fe)
1*ba07fd2fSBen Hoff // SPDX-License-Identifier: GPL-2.0-only
2*ba07fd2fSBen Hoff #include <linux/pci.h>
3*ba07fd2fSBen Hoff #include <linux/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