1/*
2 * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify,
8 * merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include "cx23882.h"
26#include "util.h"
27#include <KernelExport.h>
28#include <ByteOrder.h>
29#include <Drivers.h>
30
31
32#define TRACE_CX23882
33#ifdef TRACE_CX23882
34  #define TRACE dprintf
35#else
36  #define TRACE(a...)
37#endif
38
39// settings for hardware stream sync
40#define MPEG2_SYNC_BYTE		0x47
41#define MPEG2_PACKET_SIZE	188
42#define SYNC_PACKET_COUNT	7		// 0 and 5 don't seem to work
43
44// Line size is also used as FIFO size!
45// BYTES_PER_LINE must be a multiple of 8 and <= 4096 bytes
46#define PACKETS_PER_LINE	20
47#define BYTES_PER_LINE		(PACKETS_PER_LINE * MPEG2_PACKET_SIZE)
48
49#define SRAM_START_ADDRESS	0x180000
50#define SRAM_BASE_CMDS_TS	0x200
51#define SRAM_BASE_RISC_PROG	0x400
52#define SRAM_BASE_RISC_QUEUE 0x800
53#define SRAM_BASE_CDT		0x900
54#define SRAM_BASE_FIFO_0	0x1000
55#define SRAM_BASE_FIFO_1	0x2000
56
57// About 64 kByte DMA buffer size
58#define LINES_PER_BUFFER	16
59#define DMA_BUFFER_SIZE		(LINES_PER_BUFFER * BYTES_PER_LINE)
60
61
62static status_t	cx23882_buffers_alloc(cx23882_device *device);
63static void		cx23882_buffers_free(cx23882_device *device);
64static void		cx23882_risc_ram_setup(cx23882_device *device);
65static void		cx23882_sram_setup(cx23882_device *device);
66static void		cx23882_via_sis_fixup(cx23882_device *device);
67
68
69void
70cx23882_reset(cx23882_device *device)
71{
72	// software reset (XXX Test)
73	reg_write32(0x38c06c, 1);
74	snooze(200000);
75
76	// disable RISC controller
77	reg_write32(REG_DEV_CNTRL2, 0);
78
79	// disable TS interface DMA
80	reg_write32(REG_TS_DMA_CNTRL, 0x0);
81
82	// disable VIP interface up- & downstram DMA
83	reg_write32(REG_VIP_STREAM_EN, 0x0);
84
85	// disable host interface up- & downstram DMA
86	reg_write32(REG_HST_STREAM_EN, 0x0);
87
88	// stop all interrupts
89	reg_write32(REG_PCI_INT_MSK, 0x0);
90	reg_write32(REG_VID_INT_MSK, 0x0);
91	reg_write32(REG_AUD_INT_MSK, 0x0);
92	reg_write32(REG_TS_INT_MSK,  0x0);
93	reg_write32(REG_VIP_INT_MSK, 0x0);
94	reg_write32(REG_HST_INT_MSK, 0x0);
95	reg_write32(REG_DMA_RISC_INT_MSK, 0x0);
96
97	// clear all pending interrupts
98	reg_write32(REG_PCI_INT_STAT, 0xffffffff);
99	reg_write32(REG_VID_INT_STAT, 0xffffffff);
100	reg_write32(REG_AUD_INT_STAT, 0xffffffff);
101	reg_write32(REG_TS_INT_STAT,  0xffffffff);
102	reg_write32(REG_VIP_INT_STAT, 0xffffffff);
103	reg_write32(REG_HST_INT_STAT, 0xffffffff);
104	reg_write32(REG_DMA_RISC_INT_MSK, 0xffffffff);
105}
106
107
108status_t
109cx23882_init(cx23882_device *device)
110{
111	// assumes that cx23882_reset() has already been called
112
113	status_t err;
114
115	if ((err = cx23882_buffers_alloc(device)) < B_OK) {
116		dprintf("cx23882: Error, buffer alloc failed\n");
117		return err;
118	}
119
120	device->capture_size = DMA_BUFFER_SIZE;
121
122	cx23882_via_sis_fixup(device);
123
124	// Set FIFO thresholds, should be 0 < x <= 7
125	reg_write32(REG_PDMA_STHRSH, PDMA_ISBTHRSH_6 | PDMA_PCITHRSH_6);
126	reg_write32(REG_PDMA_DTHRSH, PDMA_ISBTHRSH_6 | PDMA_PCITHRSH_6);
127
128	// init risc programm
129	cx23882_risc_ram_setup(device);
130
131	// init sram
132	cx23882_sram_setup(device);
133
134	// Reset counter to 0
135	reg_write32(REG_TS_GP_CNT_CNTRL, 0x3);
136
137	// Line length for RISC DMA
138	reg_write32(REG_TS_LNGTH, BYTES_PER_LINE);
139
140	// Set serial interface mode
141	reg_write32(REG_TS_GEN_CONTROL, reg_read32(REG_TS_GEN_CONTROL) | TS_GEN_CONTROL_IPB_SMODE);
142
143	// Setup hardware MPEG2 fec interface
144	reg_write32(REG_HW_SOP_CONTROL, (MPEG2_SYNC_BYTE << 16) | (MPEG2_PACKET_SIZE << 4) | SYNC_PACKET_COUNT);
145
146	// Setup TSSTOP status, active low, rising and falling edge, single bit width
147	reg_write32(REG_TS_SOP_STATUS, reg_read32(REG_TS_SOP_STATUS) |  0x18000);
148	reg_write32(REG_TS_SOP_STATUS, reg_read32(REG_TS_SOP_STATUS) & ~0x06000);
149
150	// Enable interrupts for MPEG TS and all errors
151	reg_write32(REG_PCI_INT_MSK, reg_read32(REG_PCI_INT_MSK) | PCI_INT_STAT_TS_INT | 0x00fc00);
152	reg_write32(REG_TS_INT_MSK, reg_read32(REG_TS_INT_MSK) | TS_INT_STAT_TS_RISC1 | TS_INT_STAT_TS_RISC2 | 0x1f1100);
153
154	TRACE("cx23882_init done\n");
155	return B_OK;
156}
157
158
159status_t
160cx23882_terminate(cx23882_device *device)
161{
162	cx23882_reset(device);
163
164	cx23882_buffers_free(device);
165	return B_OK;
166}
167
168
169status_t
170cx23882_start_capture(cx23882_device *device)
171{
172	TRACE("cx23882_start_capture\n");
173
174	// start RISC processor and DMA
175	reg_write32(REG_DEV_CNTRL2, reg_read32(REG_DEV_CNTRL2) | DEV_CNTRL2_RUN_RISC);
176	reg_write32(REG_TS_DMA_CNTRL, reg_read32(REG_TS_DMA_CNTRL) | TS_DMA_CNTRL_TS_FIFO_EN | TS_DMA_CNTRL_TS_RISC_EN);
177	return B_OK;
178}
179
180
181status_t
182cx23882_stop_capture(cx23882_device *device)
183{
184	TRACE("cx23882_stop_capture\n");
185
186	// stop RISC processor and DMA
187	reg_write32(REG_TS_DMA_CNTRL, reg_read32(REG_TS_DMA_CNTRL) & ~(TS_DMA_CNTRL_TS_FIFO_EN | TS_DMA_CNTRL_TS_RISC_EN));
188	reg_write32(REG_DEV_CNTRL2, reg_read32(REG_DEV_CNTRL2) & ~DEV_CNTRL2_RUN_RISC);
189	return B_OK;
190}
191
192
193static inline void
194cx23882_mpegts_int(cx23882_device *device)
195{
196	uint32 mstat = reg_read32(REG_TS_INT_MSTAT);
197	reg_write32(REG_TS_INT_STAT, mstat);
198
199//	dprintf("cx23882_mpegts_int got 0x%08lx\n", mstat);
200
201	if (mstat & TS_INT_STAT_OPC_ERR) {
202		dprintf("cx23882_mpegts_int RISC opcode error\n");
203		reg_write32(REG_PCI_INT_MSK, 0);
204		return;
205	}
206
207	if ((mstat & (TS_INT_STAT_TS_RISC1 | TS_INT_STAT_TS_RISC2)) == (TS_INT_STAT_TS_RISC1 | TS_INT_STAT_TS_RISC2)) {
208		dprintf("cx23882_mpegts_int both buffers ready\n");
209		mstat = TS_INT_STAT_TS_RISC1;
210	}
211
212	if (mstat & TS_INT_STAT_TS_RISC1) {
213		int32 count;
214//		dprintf("cx23882_mpegts_int buffer 1 at %lld\n", system_time());
215		device->capture_data = device->dma_buf1_virt;
216		device->capture_end_time = system_time();
217		get_sem_count(device->capture_sem, &count);
218		if (count <= 0)
219			release_sem_etc(device->capture_sem, 1, B_DO_NOT_RESCHEDULE);
220	}
221
222	if (mstat & TS_INT_STAT_TS_RISC2) {
223		int32 count;
224//		dprintf("cx23882_mpegts_int buffer 2 at %lld\n", system_time());
225		device->capture_data = device->dma_buf2_virt;
226		device->capture_end_time = system_time();
227		get_sem_count(device->capture_sem, &count);
228		if (count <= 0)
229			release_sem_etc(device->capture_sem, 1, B_DO_NOT_RESCHEDULE);
230	}
231}
232
233
234int32
235cx23882_int(void *data)
236{
237	cx23882_device *device = data;
238	uint32 mstat;
239	uint32 wmstat;
240
241	mstat = reg_read32(REG_PCI_INT_MSTAT);
242	if (!mstat)
243		return B_UNHANDLED_INTERRUPT;
244
245	if (mstat & (PCI_INT_STAT_HST_INT | PCI_INT_STAT_VIP_INT | PCI_INT_STAT_AUD_INT | PCI_INT_STAT_VID_INT)) {
246		// serious error, these bits should not be set
247		dprintf("cx23882_int error: msk 0x%08" B_PRIx32 ", stat 0x%08" B_PRIx32
248			", mstat 0x%08" B_PRIx32 "\n", reg_read32(REG_PCI_INT_MSK),
249			reg_read32(REG_PCI_INT_STAT), mstat);
250		reg_write32(REG_PCI_INT_MSK, 0);
251		return B_HANDLED_INTERRUPT;
252	}
253
254	wmstat = mstat & ~(PCI_INT_STAT_HST_INT | PCI_INT_STAT_VIP_INT | PCI_INT_STAT_TS_INT | PCI_INT_STAT_AUD_INT | PCI_INT_STAT_VID_INT);
255	if (wmstat)
256		reg_write32(REG_PCI_INT_STAT, wmstat);
257
258	if (wmstat)
259		dprintf("cx23882_int got 0x%08" B_PRIx32 "\n", wmstat);
260
261	if (mstat & PCI_INT_STAT_TS_INT) {
262		cx23882_mpegts_int(device);
263		return B_INVOKE_SCHEDULER;
264	} else {
265		return B_HANDLED_INTERRUPT;
266	}
267}
268
269
270static status_t
271cx23882_buffers_alloc(cx23882_device *device)
272{
273	device->dma_buf1_area = alloc_mem(&device->dma_buf1_virt, &device->dma_buf1_phys, DMA_BUFFER_SIZE, B_READ_AREA, "cx23882 dma buf 1");
274	device->dma_buf2_area = alloc_mem(&device->dma_buf2_virt, &device->dma_buf2_phys, DMA_BUFFER_SIZE, B_READ_AREA, "cx23882 dma buf 2");
275	if (device->dma_buf1_area < B_OK || device->dma_buf2_area < B_OK) {
276		cx23882_buffers_free(device);
277		return B_NO_MEMORY;
278	}
279	return B_OK;
280}
281
282
283static void
284cx23882_buffers_free(cx23882_device *device)
285{
286	if (device->dma_buf1_area >= 0)
287		delete_area(device->dma_buf1_area);
288	if (device->dma_buf2_area >= 0)
289		delete_area(device->dma_buf2_area);
290	device->dma_buf1_area = -1;
291	device->dma_buf2_area = -1;
292}
293
294
295static void
296cx23882_sram_setup(cx23882_device *device)
297{
298	dprintf("cx23882_sram_setup enter\n");
299
300	// setup CDT entries for both FIFOs
301	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CDT, SRAM_START_ADDRESS + SRAM_BASE_FIFO_0);
302	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CDT + 16, SRAM_START_ADDRESS + SRAM_BASE_FIFO_1);
303
304	// setup CDMS
305	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x00, SRAM_START_ADDRESS + SRAM_BASE_RISC_PROG);
306	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x04, SRAM_START_ADDRESS + SRAM_BASE_CDT);
307	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x08, (2 * 16) / 8); // FIFO count = 2
308	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x0c, SRAM_START_ADDRESS + SRAM_BASE_RISC_QUEUE);
309	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x10, 0x80000000 | (0x100 / 4));
310
311	// setup DMA registers
312	reg_write32(REG_DMA28_PTR1, SRAM_START_ADDRESS + SRAM_BASE_FIFO_0);
313	reg_write32(REG_DMA28_PTR2, SRAM_START_ADDRESS + SRAM_BASE_CDT);
314	reg_write32(REG_DMA28_CNT1, BYTES_PER_LINE / 8);
315	reg_write32(REG_DMA28_CNT2, (2 * 16) / 8); // FIFO count = 2
316
317	dprintf("cx23882_sram_setup leave\n");
318}
319
320
321static void
322cx23882_risc_ram_setup(cx23882_device *device)
323{
324	char *start = (char *)(device->regs) + SRAM_START_ADDRESS + SRAM_BASE_RISC_PROG;
325	volatile uint32 *rp = (volatile uint32 *)start;
326	int i;
327
328	#define set_opcode(a) (*rp++) = B_HOST_TO_LENDIAN_INT32((a))
329
330	dprintf("cx23882_risc_ram_setup enter\n");
331
332	// sync
333	set_opcode(RISC_RESYNC | 0);
334
335	// copy buffer 1
336	for (i = 0; i < LINES_PER_BUFFER; i++) {
337		set_opcode(RISC_WRITE | RISC_SOL | RISC_EOL | BYTES_PER_LINE);
338		set_opcode((unsigned long)device->dma_buf1_phys + i * BYTES_PER_LINE);
339	}
340
341	// execute IRQ 1
342	set_opcode(RISC_SKIP | RISC_IRQ1 | RISC_SOL | 0);
343
344	// copy buffer 2
345	for (i = 0; i < LINES_PER_BUFFER; i++) {
346		set_opcode(RISC_WRITE | RISC_SOL | RISC_EOL | BYTES_PER_LINE);
347		set_opcode((unsigned long)device->dma_buf2_phys + i * BYTES_PER_LINE);
348	}
349
350	// execute IRQ 2
351	set_opcode(RISC_SKIP | RISC_IRQ2 | RISC_SOL | 0);
352
353	// jmp to start, but skip sync instruction
354	set_opcode(RISC_JUMP | RISC_SRP);
355	set_opcode(SRAM_START_ADDRESS + SRAM_BASE_RISC_PROG + 4);
356
357	#undef set_opcode
358
359	dprintf("cx23882_risc_ram_setup leave\n");
360}
361
362
363static void
364cx23882_via_sis_fixup(cx23882_device *device)
365{
366	uint16 host_vendor;
367	uint32 dev_cntrl1;
368
369	host_vendor = gPci->read_pci_config(0, 0, 0, PCI_vendor_id, 2);
370	dev_cntrl1 = reg_read32(REG_F2_DEV_CNTRL1);
371
372	if (host_vendor == PCI_VENDOR_VIA || host_vendor == PCI_VENDOR_SIS) {
373		dprintf("cx23882: enabling VIA/SIS compatibility mode\n");
374		reg_write32(REG_F2_DEV_CNTRL1, dev_cntrl1 | F2_DEV_CNTRL1_EN_VSFX);
375	} else {
376		dprintf("cx23882: disabling VIA/SIS compatibility mode\n");
377		reg_write32(REG_F2_DEV_CNTRL1, dev_cntrl1 & ~F2_DEV_CNTRL1_EN_VSFX);
378	}
379}
380