xref: /haiku/src/add-ons/kernel/drivers/dvb/cx23882/cx23882.c (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
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 
62 static status_t	cx23882_buffers_alloc(cx23882_device *device);
63 static void		cx23882_buffers_free(cx23882_device *device);
64 static void		cx23882_risc_ram_setup(cx23882_device *device);
65 static void		cx23882_sram_setup(cx23882_device *device);
66 static void		cx23882_via_sis_fixup(cx23882_device *device);
67 
68 
69 void
70 cx23882_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 
108 status_t
109 cx23882_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 
159 status_t
160 cx23882_terminate(cx23882_device *device)
161 {
162 	cx23882_reset(device);
163 
164 	cx23882_buffers_free(device);
165 	return B_OK;
166 }
167 
168 
169 status_t
170 cx23882_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 
181 status_t
182 cx23882_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 
193 static inline void
194 cx23882_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 %Ld\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 %Ld\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 
234 int32
235 cx23882_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%08lx, stat 0x%08lx, mstat 0x%08lx\n", reg_read32(REG_PCI_INT_MSK), reg_read32(REG_PCI_INT_STAT), mstat);
248 		reg_write32(REG_PCI_INT_MSK, 0);
249 		return B_HANDLED_INTERRUPT;
250 	}
251 
252 	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);
253 	if (wmstat)
254 		reg_write32(REG_PCI_INT_STAT, wmstat);
255 
256 	if (wmstat)
257 		dprintf("cx23882_int got 0x%08lx\n", wmstat);
258 
259 	if (mstat & PCI_INT_STAT_TS_INT) {
260 		cx23882_mpegts_int(device);
261 		return B_INVOKE_SCHEDULER;
262 	} else {
263 		return B_HANDLED_INTERRUPT;
264 	}
265 }
266 
267 
268 static status_t
269 cx23882_buffers_alloc(cx23882_device *device)
270 {
271 	device->dma_buf1_area = alloc_mem(&device->dma_buf1_virt, &device->dma_buf1_phys, DMA_BUFFER_SIZE, B_READ_AREA, "cx23882 dma buf 1");
272 	device->dma_buf2_area = alloc_mem(&device->dma_buf2_virt, &device->dma_buf2_phys, DMA_BUFFER_SIZE, B_READ_AREA, "cx23882 dma buf 2");
273 	if (device->dma_buf1_area < B_OK || device->dma_buf2_area < B_OK) {
274 		cx23882_buffers_free(device);
275 		return B_NO_MEMORY;
276 	}
277 	return B_OK;
278 }
279 
280 
281 static void
282 cx23882_buffers_free(cx23882_device *device)
283 {
284 	if (device->dma_buf1_area >= 0)
285 		delete_area(device->dma_buf1_area);
286 	if (device->dma_buf2_area >= 0)
287 		delete_area(device->dma_buf2_area);
288 	device->dma_buf1_area = -1;
289 	device->dma_buf2_area = -1;
290 }
291 
292 
293 static void
294 cx23882_sram_setup(cx23882_device *device)
295 {
296 	dprintf("cx23882_sram_setup enter\n");
297 
298 	// setup CDT entries for both FIFOs
299 	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CDT, SRAM_START_ADDRESS + SRAM_BASE_FIFO_0);
300 	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CDT + 16, SRAM_START_ADDRESS + SRAM_BASE_FIFO_1);
301 
302 	// setup CDMS
303 	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x00, SRAM_START_ADDRESS + SRAM_BASE_RISC_PROG);
304 	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x04, SRAM_START_ADDRESS + SRAM_BASE_CDT);
305 	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x08, (2 * 16) / 8); // FIFO count = 2
306 	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x0c, SRAM_START_ADDRESS + SRAM_BASE_RISC_QUEUE);
307 	reg_write32(SRAM_START_ADDRESS + SRAM_BASE_CMDS_TS + 0x10, 0x80000000 | (0x100 / 4));
308 
309 	// setup DMA registers
310 	reg_write32(REG_DMA28_PTR1, SRAM_START_ADDRESS + SRAM_BASE_FIFO_0);
311 	reg_write32(REG_DMA28_PTR2, SRAM_START_ADDRESS + SRAM_BASE_CDT);
312 	reg_write32(REG_DMA28_CNT1, BYTES_PER_LINE / 8);
313 	reg_write32(REG_DMA28_CNT2, (2 * 16) / 8); // FIFO count = 2
314 
315 	dprintf("cx23882_sram_setup leave\n");
316 }
317 
318 
319 static void
320 cx23882_risc_ram_setup(cx23882_device *device)
321 {
322 	char *start = (char *)(device->regs) + SRAM_START_ADDRESS + SRAM_BASE_RISC_PROG;
323 	volatile uint32 *rp = (volatile uint32 *)start;
324 	int i;
325 
326 	#define set_opcode(a) (*rp++) = B_HOST_TO_LENDIAN_INT32((a))
327 
328 	dprintf("cx23882_risc_ram_setup enter\n");
329 
330 	// sync
331 	set_opcode(RISC_RESYNC | 0);
332 
333 	// copy buffer 1
334 	for (i = 0; i < LINES_PER_BUFFER; i++) {
335 		set_opcode(RISC_WRITE | RISC_SOL | RISC_EOL | BYTES_PER_LINE);
336 		set_opcode((unsigned long)device->dma_buf1_phys + i * BYTES_PER_LINE);
337 	}
338 
339 	// execute IRQ 1
340 	set_opcode(RISC_SKIP | RISC_IRQ1 | RISC_SOL | 0);
341 
342 	// copy buffer 2
343 	for (i = 0; i < LINES_PER_BUFFER; i++) {
344 		set_opcode(RISC_WRITE | RISC_SOL | RISC_EOL | BYTES_PER_LINE);
345 		set_opcode((unsigned long)device->dma_buf2_phys + i * BYTES_PER_LINE);
346 	}
347 
348 	// execute IRQ 2
349 	set_opcode(RISC_SKIP | RISC_IRQ2 | RISC_SOL | 0);
350 
351 	// jmp to start, but skip sync instruction
352 	set_opcode(RISC_JUMP | RISC_SRP);
353 	set_opcode(SRAM_START_ADDRESS + SRAM_BASE_RISC_PROG + 4);
354 
355 	#undef set_opcode
356 
357 	dprintf("cx23882_risc_ram_setup leave\n");
358 }
359 
360 
361 static void
362 cx23882_via_sis_fixup(cx23882_device *device)
363 {
364 	uint16 host_vendor;
365 	uint32 dev_cntrl1;
366 
367 	host_vendor = gPci->read_pci_config(0, 0, 0, PCI_vendor_id, 2);
368 	dev_cntrl1 = reg_read32(REG_F2_DEV_CNTRL1);
369 
370 	if (host_vendor == PCI_VENDOR_VIA || host_vendor == PCI_VENDOR_SIS) {
371 		dprintf("cx23882: enabling VIA/SIS compatibility mode\n");
372 		reg_write32(REG_F2_DEV_CNTRL1, dev_cntrl1 | F2_DEV_CNTRL1_EN_VSFX);
373 	} else {
374 		dprintf("cx23882: disabling VIA/SIS compatibility mode\n");
375 		reg_write32(REG_F2_DEV_CNTRL1, dev_cntrl1 & ~F2_DEV_CNTRL1_EN_VSFX);
376 	}
377 }
378