xref: /linux/drivers/media/usb/pvrusb2/pvrusb2-encoder.c (revision 1ad371deb9b0be142dca205611a56854a37fc48d)
10c0d06caSMauro Carvalho Chehab /*
20c0d06caSMauro Carvalho Chehab  *
30c0d06caSMauro Carvalho Chehab  *
40c0d06caSMauro Carvalho Chehab  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
50c0d06caSMauro Carvalho Chehab  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
60c0d06caSMauro Carvalho Chehab  *
70c0d06caSMauro Carvalho Chehab  *  This program is free software; you can redistribute it and/or modify
80c0d06caSMauro Carvalho Chehab  *  it under the terms of the GNU General Public License as published by
90c0d06caSMauro Carvalho Chehab  *  the Free Software Foundation; either version 2 of the License
100c0d06caSMauro Carvalho Chehab  *
110c0d06caSMauro Carvalho Chehab  *  This program is distributed in the hope that it will be useful,
120c0d06caSMauro Carvalho Chehab  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
130c0d06caSMauro Carvalho Chehab  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
140c0d06caSMauro Carvalho Chehab  *  GNU General Public License for more details.
150c0d06caSMauro Carvalho Chehab  *
160c0d06caSMauro Carvalho Chehab  */
170c0d06caSMauro Carvalho Chehab 
180c0d06caSMauro Carvalho Chehab #include <linux/device.h>   // for linux/firmware.h
190c0d06caSMauro Carvalho Chehab #include <linux/firmware.h>
200c0d06caSMauro Carvalho Chehab #include "pvrusb2-util.h"
210c0d06caSMauro Carvalho Chehab #include "pvrusb2-encoder.h"
220c0d06caSMauro Carvalho Chehab #include "pvrusb2-hdw-internal.h"
230c0d06caSMauro Carvalho Chehab #include "pvrusb2-debug.h"
240c0d06caSMauro Carvalho Chehab #include "pvrusb2-fx2-cmd.h"
250c0d06caSMauro Carvalho Chehab 
260c0d06caSMauro Carvalho Chehab 
270c0d06caSMauro Carvalho Chehab 
280c0d06caSMauro Carvalho Chehab /* Firmware mailbox flags - definitions found from ivtv */
290c0d06caSMauro Carvalho Chehab #define IVTV_MBOX_FIRMWARE_DONE 0x00000004
300c0d06caSMauro Carvalho Chehab #define IVTV_MBOX_DRIVER_DONE 0x00000002
310c0d06caSMauro Carvalho Chehab #define IVTV_MBOX_DRIVER_BUSY 0x00000001
320c0d06caSMauro Carvalho Chehab 
330c0d06caSMauro Carvalho Chehab #define MBOX_BASE 0x44
340c0d06caSMauro Carvalho Chehab 
350c0d06caSMauro Carvalho Chehab 
360c0d06caSMauro Carvalho Chehab static int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
370c0d06caSMauro Carvalho Chehab 				    unsigned int offs,
380c0d06caSMauro Carvalho Chehab 				    const u32 *data, unsigned int dlen)
390c0d06caSMauro Carvalho Chehab {
400c0d06caSMauro Carvalho Chehab 	unsigned int idx,addr;
410c0d06caSMauro Carvalho Chehab 	unsigned int bAddr;
420c0d06caSMauro Carvalho Chehab 	int ret;
430c0d06caSMauro Carvalho Chehab 	unsigned int chunkCnt;
440c0d06caSMauro Carvalho Chehab 
450c0d06caSMauro Carvalho Chehab 	/*
460c0d06caSMauro Carvalho Chehab 
470c0d06caSMauro Carvalho Chehab 	Format: First byte must be 0x01.  Remaining 32 bit words are
480c0d06caSMauro Carvalho Chehab 	spread out into chunks of 7 bytes each, with the first 4 bytes
490c0d06caSMauro Carvalho Chehab 	being the data word (little endian), and the next 3 bytes
500c0d06caSMauro Carvalho Chehab 	being the address where that data word is to be written (big
510c0d06caSMauro Carvalho Chehab 	endian).  Repeat request for additional words, with offset
520c0d06caSMauro Carvalho Chehab 	adjusted accordingly.
530c0d06caSMauro Carvalho Chehab 
540c0d06caSMauro Carvalho Chehab 	*/
550c0d06caSMauro Carvalho Chehab 	while (dlen) {
560c0d06caSMauro Carvalho Chehab 		chunkCnt = 8;
570c0d06caSMauro Carvalho Chehab 		if (chunkCnt > dlen) chunkCnt = dlen;
580c0d06caSMauro Carvalho Chehab 		memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
590c0d06caSMauro Carvalho Chehab 		bAddr = 0;
600c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD;
610c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < chunkCnt; idx++) {
620c0d06caSMauro Carvalho Chehab 			addr = idx + offs;
630c0d06caSMauro Carvalho Chehab 			hdw->cmd_buffer[bAddr+6] = (addr & 0xffu);
640c0d06caSMauro Carvalho Chehab 			hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu);
650c0d06caSMauro Carvalho Chehab 			hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu);
660c0d06caSMauro Carvalho Chehab 			PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]);
670c0d06caSMauro Carvalho Chehab 			bAddr += 7;
680c0d06caSMauro Carvalho Chehab 		}
690c0d06caSMauro Carvalho Chehab 		ret = pvr2_send_request(hdw,
700c0d06caSMauro Carvalho Chehab 					hdw->cmd_buffer,1+(chunkCnt*7),
710c0d06caSMauro Carvalho Chehab 					NULL,0);
720c0d06caSMauro Carvalho Chehab 		if (ret) return ret;
730c0d06caSMauro Carvalho Chehab 		data += chunkCnt;
740c0d06caSMauro Carvalho Chehab 		dlen -= chunkCnt;
750c0d06caSMauro Carvalho Chehab 		offs += chunkCnt;
760c0d06caSMauro Carvalho Chehab 	}
770c0d06caSMauro Carvalho Chehab 
780c0d06caSMauro Carvalho Chehab 	return 0;
790c0d06caSMauro Carvalho Chehab }
800c0d06caSMauro Carvalho Chehab 
810c0d06caSMauro Carvalho Chehab 
820c0d06caSMauro Carvalho Chehab static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,
830c0d06caSMauro Carvalho Chehab 				   unsigned int offs,
840c0d06caSMauro Carvalho Chehab 				   u32 *data, unsigned int dlen)
850c0d06caSMauro Carvalho Chehab {
860c0d06caSMauro Carvalho Chehab 	unsigned int idx;
870c0d06caSMauro Carvalho Chehab 	int ret;
880c0d06caSMauro Carvalho Chehab 	unsigned int chunkCnt;
890c0d06caSMauro Carvalho Chehab 
900c0d06caSMauro Carvalho Chehab 	/*
910c0d06caSMauro Carvalho Chehab 
920c0d06caSMauro Carvalho Chehab 	Format: First byte must be 0x02 (status check) or 0x28 (read
930c0d06caSMauro Carvalho Chehab 	back block of 32 bit words).  Next 6 bytes must be zero,
940c0d06caSMauro Carvalho Chehab 	followed by a single byte of MBOX_BASE+offset for portion to
950c0d06caSMauro Carvalho Chehab 	be read.  Returned data is packed set of 32 bits words that
960c0d06caSMauro Carvalho Chehab 	were read.
970c0d06caSMauro Carvalho Chehab 
980c0d06caSMauro Carvalho Chehab 	*/
990c0d06caSMauro Carvalho Chehab 
1000c0d06caSMauro Carvalho Chehab 	while (dlen) {
1010c0d06caSMauro Carvalho Chehab 		chunkCnt = 16;
1020c0d06caSMauro Carvalho Chehab 		if (chunkCnt > dlen) chunkCnt = dlen;
1030c0d06caSMauro Carvalho Chehab 		if (chunkCnt < 16) chunkCnt = 1;
1040c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[0] =
1050c0d06caSMauro Carvalho Chehab 			((chunkCnt == 1) ?
1060c0d06caSMauro Carvalho Chehab 			 FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES);
1070c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[1] = 0;
1080c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[2] = 0;
1090c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[3] = 0;
1100c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[4] = 0;
1110c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[5] = ((offs>>16) & 0xffu);
1120c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[6] = ((offs>>8) & 0xffu);
1130c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[7] = (offs & 0xffu);
1140c0d06caSMauro Carvalho Chehab 		ret = pvr2_send_request(hdw,
1150c0d06caSMauro Carvalho Chehab 					hdw->cmd_buffer,8,
1160c0d06caSMauro Carvalho Chehab 					hdw->cmd_buffer,
1170c0d06caSMauro Carvalho Chehab 					(chunkCnt == 1 ? 4 : 16 * 4));
1180c0d06caSMauro Carvalho Chehab 		if (ret) return ret;
1190c0d06caSMauro Carvalho Chehab 
1200c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < chunkCnt; idx++) {
1210c0d06caSMauro Carvalho Chehab 			data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4);
1220c0d06caSMauro Carvalho Chehab 		}
1230c0d06caSMauro Carvalho Chehab 		data += chunkCnt;
1240c0d06caSMauro Carvalho Chehab 		dlen -= chunkCnt;
1250c0d06caSMauro Carvalho Chehab 		offs += chunkCnt;
1260c0d06caSMauro Carvalho Chehab 	}
1270c0d06caSMauro Carvalho Chehab 
1280c0d06caSMauro Carvalho Chehab 	return 0;
1290c0d06caSMauro Carvalho Chehab }
1300c0d06caSMauro Carvalho Chehab 
1310c0d06caSMauro Carvalho Chehab 
1320c0d06caSMauro Carvalho Chehab /* This prototype is set up to be compatible with the
1330c0d06caSMauro Carvalho Chehab    cx2341x_mbox_func prototype in cx2341x.h, which should be in
1340c0d06caSMauro Carvalho Chehab    kernels 2.6.18 or later.  We do this so that we can enable
1350c0d06caSMauro Carvalho Chehab    cx2341x.ko to write to our encoder (by handing it a pointer to this
1360c0d06caSMauro Carvalho Chehab    function).  For earlier kernels this doesn't really matter. */
1370c0d06caSMauro Carvalho Chehab static int pvr2_encoder_cmd(void *ctxt,
1380c0d06caSMauro Carvalho Chehab 			    u32 cmd,
1390c0d06caSMauro Carvalho Chehab 			    int arg_cnt_send,
1400c0d06caSMauro Carvalho Chehab 			    int arg_cnt_recv,
1410c0d06caSMauro Carvalho Chehab 			    u32 *argp)
1420c0d06caSMauro Carvalho Chehab {
1430c0d06caSMauro Carvalho Chehab 	unsigned int poll_count;
1440c0d06caSMauro Carvalho Chehab 	unsigned int try_count = 0;
1450c0d06caSMauro Carvalho Chehab 	int retry_flag;
1460c0d06caSMauro Carvalho Chehab 	int ret = 0;
1470c0d06caSMauro Carvalho Chehab 	unsigned int idx;
1480c0d06caSMauro Carvalho Chehab 	/* These sizes look to be limited by the FX2 firmware implementation */
1490c0d06caSMauro Carvalho Chehab 	u32 wrData[16];
1500c0d06caSMauro Carvalho Chehab 	u32 rdData[16];
1510c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt;
1520c0d06caSMauro Carvalho Chehab 
1530c0d06caSMauro Carvalho Chehab 
1540c0d06caSMauro Carvalho Chehab 	/*
1550c0d06caSMauro Carvalho Chehab 
1560c0d06caSMauro Carvalho Chehab 	The encoder seems to speak entirely using blocks 32 bit words.
1570c0d06caSMauro Carvalho Chehab 	In ivtv driver terms, this is a mailbox at MBOX_BASE which we
1580c0d06caSMauro Carvalho Chehab 	populate with data and watch what the hardware does with it.
1590c0d06caSMauro Carvalho Chehab 	The first word is a set of flags used to control the
1600c0d06caSMauro Carvalho Chehab 	transaction, the second word is the command to execute, the
1610c0d06caSMauro Carvalho Chehab 	third byte is zero (ivtv driver suggests that this is some
1620c0d06caSMauro Carvalho Chehab 	kind of return value), and the fourth byte is a specified
1630c0d06caSMauro Carvalho Chehab 	timeout (windows driver always uses 0x00060000 except for one
1640c0d06caSMauro Carvalho Chehab 	case when it is zero).  All successive words are the argument
1650c0d06caSMauro Carvalho Chehab 	words for the command.
1660c0d06caSMauro Carvalho Chehab 
1670c0d06caSMauro Carvalho Chehab 	First, write out the entire set of words, with the first word
1680c0d06caSMauro Carvalho Chehab 	being zero.
1690c0d06caSMauro Carvalho Chehab 
1700c0d06caSMauro Carvalho Chehab 	Next, write out just the first word again, but set it to
1710c0d06caSMauro Carvalho Chehab 	IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which
1720c0d06caSMauro Carvalho Chehab 	probably means "go").
1730c0d06caSMauro Carvalho Chehab 
1740c0d06caSMauro Carvalho Chehab 	Next, read back the return count words.  Check the first word,
1750c0d06caSMauro Carvalho Chehab 	which should have IVTV_MBOX_FIRMWARE_DONE set.  If however
1760c0d06caSMauro Carvalho Chehab 	that bit is not set, then the command isn't done so repeat the
1770c0d06caSMauro Carvalho Chehab 	read until it is set.
1780c0d06caSMauro Carvalho Chehab 
1790c0d06caSMauro Carvalho Chehab 	Finally, write out just the first word again, but set it to
1800c0d06caSMauro Carvalho Chehab 	0x0 this time (which probably means "idle").
1810c0d06caSMauro Carvalho Chehab 
1820c0d06caSMauro Carvalho Chehab 	*/
1830c0d06caSMauro Carvalho Chehab 
1840c0d06caSMauro Carvalho Chehab 	if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) {
1850c0d06caSMauro Carvalho Chehab 		pvr2_trace(
1860c0d06caSMauro Carvalho Chehab 			PVR2_TRACE_ERROR_LEGS,
18796292c89SMauro Carvalho Chehab 			"Failed to write cx23416 command - too many input arguments (was given %u limit %lu)",
1880c0d06caSMauro Carvalho Chehab 			arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4);
1890c0d06caSMauro Carvalho Chehab 		return -EINVAL;
1900c0d06caSMauro Carvalho Chehab 	}
1910c0d06caSMauro Carvalho Chehab 
1920c0d06caSMauro Carvalho Chehab 	if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) {
1930c0d06caSMauro Carvalho Chehab 		pvr2_trace(
1940c0d06caSMauro Carvalho Chehab 			PVR2_TRACE_ERROR_LEGS,
19596292c89SMauro Carvalho Chehab 			"Failed to write cx23416 command - too many return arguments (was given %u limit %lu)",
1960c0d06caSMauro Carvalho Chehab 			arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4);
1970c0d06caSMauro Carvalho Chehab 		return -EINVAL;
1980c0d06caSMauro Carvalho Chehab 	}
1990c0d06caSMauro Carvalho Chehab 
2000c0d06caSMauro Carvalho Chehab 
201*1ad371deSMauro Carvalho Chehab 	LOCK_TAKE(hdw->ctl_lock); while (1) {
2020c0d06caSMauro Carvalho Chehab 
2030c0d06caSMauro Carvalho Chehab 		if (!hdw->state_encoder_ok) {
2040c0d06caSMauro Carvalho Chehab 			ret = -EIO;
2050c0d06caSMauro Carvalho Chehab 			break;
2060c0d06caSMauro Carvalho Chehab 		}
2070c0d06caSMauro Carvalho Chehab 
2080c0d06caSMauro Carvalho Chehab 		retry_flag = 0;
2090c0d06caSMauro Carvalho Chehab 		try_count++;
2100c0d06caSMauro Carvalho Chehab 		ret = 0;
2110c0d06caSMauro Carvalho Chehab 		wrData[0] = 0;
2120c0d06caSMauro Carvalho Chehab 		wrData[1] = cmd;
2130c0d06caSMauro Carvalho Chehab 		wrData[2] = 0;
2140c0d06caSMauro Carvalho Chehab 		wrData[3] = 0x00060000;
2150c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < arg_cnt_send; idx++) {
2160c0d06caSMauro Carvalho Chehab 			wrData[idx+4] = argp[idx];
2170c0d06caSMauro Carvalho Chehab 		}
2180c0d06caSMauro Carvalho Chehab 		for (; idx < ARRAY_SIZE(wrData) - 4; idx++) {
2190c0d06caSMauro Carvalho Chehab 			wrData[idx+4] = 0;
2200c0d06caSMauro Carvalho Chehab 		}
2210c0d06caSMauro Carvalho Chehab 
2220c0d06caSMauro Carvalho Chehab 		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx);
2230c0d06caSMauro Carvalho Chehab 		if (ret) break;
2240c0d06caSMauro Carvalho Chehab 		wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
2250c0d06caSMauro Carvalho Chehab 		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
2260c0d06caSMauro Carvalho Chehab 		if (ret) break;
2270c0d06caSMauro Carvalho Chehab 		poll_count = 0;
2280c0d06caSMauro Carvalho Chehab 		while (1) {
2290c0d06caSMauro Carvalho Chehab 			poll_count++;
2300c0d06caSMauro Carvalho Chehab 			ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData,
2310c0d06caSMauro Carvalho Chehab 						      arg_cnt_recv+4);
2320c0d06caSMauro Carvalho Chehab 			if (ret) {
2330c0d06caSMauro Carvalho Chehab 				break;
2340c0d06caSMauro Carvalho Chehab 			}
2350c0d06caSMauro Carvalho Chehab 			if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
2360c0d06caSMauro Carvalho Chehab 				break;
2370c0d06caSMauro Carvalho Chehab 			}
2380c0d06caSMauro Carvalho Chehab 			if (rdData[0] && (poll_count < 1000)) continue;
2390c0d06caSMauro Carvalho Chehab 			if (!rdData[0]) {
2400c0d06caSMauro Carvalho Chehab 				retry_flag = !0;
2410c0d06caSMauro Carvalho Chehab 				pvr2_trace(
2420c0d06caSMauro Carvalho Chehab 					PVR2_TRACE_ERROR_LEGS,
24396292c89SMauro Carvalho Chehab 					"Encoder timed out waiting for us; arranging to retry");
2440c0d06caSMauro Carvalho Chehab 			} else {
2450c0d06caSMauro Carvalho Chehab 				pvr2_trace(
2460c0d06caSMauro Carvalho Chehab 					PVR2_TRACE_ERROR_LEGS,
24796292c89SMauro Carvalho Chehab 					"***WARNING*** device's encoder appears to be stuck (status=0x%08x)",
24896292c89SMauro Carvalho Chehab rdData[0]);
2490c0d06caSMauro Carvalho Chehab 			}
2500c0d06caSMauro Carvalho Chehab 			pvr2_trace(
2510c0d06caSMauro Carvalho Chehab 				PVR2_TRACE_ERROR_LEGS,
2520c0d06caSMauro Carvalho Chehab 				"Encoder command: 0x%02x",cmd);
2530c0d06caSMauro Carvalho Chehab 			for (idx = 4; idx < arg_cnt_send; idx++) {
2540c0d06caSMauro Carvalho Chehab 				pvr2_trace(
2550c0d06caSMauro Carvalho Chehab 					PVR2_TRACE_ERROR_LEGS,
2560c0d06caSMauro Carvalho Chehab 					"Encoder arg%d: 0x%08x",
2570c0d06caSMauro Carvalho Chehab 					idx-3,wrData[idx]);
2580c0d06caSMauro Carvalho Chehab 			}
2590c0d06caSMauro Carvalho Chehab 			ret = -EBUSY;
2600c0d06caSMauro Carvalho Chehab 			break;
2610c0d06caSMauro Carvalho Chehab 		}
2620c0d06caSMauro Carvalho Chehab 		if (retry_flag) {
2630c0d06caSMauro Carvalho Chehab 			if (try_count < 20) continue;
2640c0d06caSMauro Carvalho Chehab 			pvr2_trace(
2650c0d06caSMauro Carvalho Chehab 				PVR2_TRACE_ERROR_LEGS,
2660c0d06caSMauro Carvalho Chehab 				"Too many retries...");
2670c0d06caSMauro Carvalho Chehab 			ret = -EBUSY;
2680c0d06caSMauro Carvalho Chehab 		}
2690c0d06caSMauro Carvalho Chehab 		if (ret) {
2700c0d06caSMauro Carvalho Chehab 			del_timer_sync(&hdw->encoder_run_timer);
2710c0d06caSMauro Carvalho Chehab 			hdw->state_encoder_ok = 0;
2720c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_STBITS,
2730c0d06caSMauro Carvalho Chehab 				   "State bit %s <-- %s",
2740c0d06caSMauro Carvalho Chehab 				   "state_encoder_ok",
2750c0d06caSMauro Carvalho Chehab 				   (hdw->state_encoder_ok ? "true" : "false"));
2760c0d06caSMauro Carvalho Chehab 			if (hdw->state_encoder_runok) {
2770c0d06caSMauro Carvalho Chehab 				hdw->state_encoder_runok = 0;
2780c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_STBITS,
2790c0d06caSMauro Carvalho Chehab 				   "State bit %s <-- %s",
2800c0d06caSMauro Carvalho Chehab 					   "state_encoder_runok",
2810c0d06caSMauro Carvalho Chehab 					   (hdw->state_encoder_runok ?
2820c0d06caSMauro Carvalho Chehab 					    "true" : "false"));
2830c0d06caSMauro Carvalho Chehab 			}
2840c0d06caSMauro Carvalho Chehab 			pvr2_trace(
2850c0d06caSMauro Carvalho Chehab 				PVR2_TRACE_ERROR_LEGS,
28696292c89SMauro Carvalho Chehab 				"Giving up on command.  This is normally recovered via a firmware reload and re-initialization; concern is only warranted if this happens repeatedly and rapidly.");
2870c0d06caSMauro Carvalho Chehab 			break;
2880c0d06caSMauro Carvalho Chehab 		}
2890c0d06caSMauro Carvalho Chehab 		wrData[0] = 0x7;
2900c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < arg_cnt_recv; idx++) {
2910c0d06caSMauro Carvalho Chehab 			argp[idx] = rdData[idx+4];
2920c0d06caSMauro Carvalho Chehab 		}
2930c0d06caSMauro Carvalho Chehab 
2940c0d06caSMauro Carvalho Chehab 		wrData[0] = 0x0;
2950c0d06caSMauro Carvalho Chehab 		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
296*1ad371deSMauro Carvalho Chehab 		break;
2970c0d06caSMauro Carvalho Chehab 
298*1ad371deSMauro Carvalho Chehab 	}; LOCK_GIVE(hdw->ctl_lock);
2990c0d06caSMauro Carvalho Chehab 
3000c0d06caSMauro Carvalho Chehab 	return ret;
3010c0d06caSMauro Carvalho Chehab }
3020c0d06caSMauro Carvalho Chehab 
3030c0d06caSMauro Carvalho Chehab 
3040c0d06caSMauro Carvalho Chehab static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd,
3050c0d06caSMauro Carvalho Chehab 			     int args, ...)
3060c0d06caSMauro Carvalho Chehab {
3070c0d06caSMauro Carvalho Chehab 	va_list vl;
3080c0d06caSMauro Carvalho Chehab 	unsigned int idx;
3090c0d06caSMauro Carvalho Chehab 	u32 data[12];
3100c0d06caSMauro Carvalho Chehab 
3110c0d06caSMauro Carvalho Chehab 	if (args > ARRAY_SIZE(data)) {
3120c0d06caSMauro Carvalho Chehab 		pvr2_trace(
3130c0d06caSMauro Carvalho Chehab 			PVR2_TRACE_ERROR_LEGS,
31496292c89SMauro Carvalho Chehab 			"Failed to write cx23416 command - too many arguments (was given %u limit %lu)",
3150c0d06caSMauro Carvalho Chehab 			args, (long unsigned) ARRAY_SIZE(data));
3160c0d06caSMauro Carvalho Chehab 		return -EINVAL;
3170c0d06caSMauro Carvalho Chehab 	}
3180c0d06caSMauro Carvalho Chehab 
3190c0d06caSMauro Carvalho Chehab 	va_start(vl, args);
3200c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < args; idx++) {
3210c0d06caSMauro Carvalho Chehab 		data[idx] = va_arg(vl, u32);
3220c0d06caSMauro Carvalho Chehab 	}
3230c0d06caSMauro Carvalho Chehab 	va_end(vl);
3240c0d06caSMauro Carvalho Chehab 
3250c0d06caSMauro Carvalho Chehab 	return pvr2_encoder_cmd(hdw,cmd,args,0,data);
3260c0d06caSMauro Carvalho Chehab }
3270c0d06caSMauro Carvalho Chehab 
3280c0d06caSMauro Carvalho Chehab 
3290c0d06caSMauro Carvalho Chehab /* This implements some extra setup for the encoder that seems to be
3300c0d06caSMauro Carvalho Chehab    specific to the PVR USB2 hardware. */
3310c0d06caSMauro Carvalho Chehab static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
3320c0d06caSMauro Carvalho Chehab {
3330c0d06caSMauro Carvalho Chehab 	int ret = 0;
3340c0d06caSMauro Carvalho Chehab 	int encMisc3Arg = 0;
3350c0d06caSMauro Carvalho Chehab 
3360c0d06caSMauro Carvalho Chehab #if 0
3370c0d06caSMauro Carvalho Chehab 	/* This inexplicable bit happens in the Hauppauge windows
3380c0d06caSMauro Carvalho Chehab 	   driver (for both 24xxx and 29xxx devices).  However I
3390c0d06caSMauro Carvalho Chehab 	   currently see no difference in behavior with or without
3400c0d06caSMauro Carvalho Chehab 	   this stuff.  Leave this here as a note of its existence,
3410c0d06caSMauro Carvalho Chehab 	   but don't use it. */
3420c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->ctl_lock); do {
3430c0d06caSMauro Carvalho Chehab 		u32 dat[1];
3440c0d06caSMauro Carvalho Chehab 		dat[0] = 0x80000640;
3450c0d06caSMauro Carvalho Chehab 		pvr2_encoder_write_words(hdw,0x01fe,dat,1);
3460c0d06caSMauro Carvalho Chehab 		pvr2_encoder_write_words(hdw,0x023e,dat,1);
3470c0d06caSMauro Carvalho Chehab 	} while(0); LOCK_GIVE(hdw->ctl_lock);
3480c0d06caSMauro Carvalho Chehab #endif
3490c0d06caSMauro Carvalho Chehab 
3500c0d06caSMauro Carvalho Chehab 	/* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver
3510c0d06caSMauro Carvalho Chehab 	   sends the following list of ENC_MISC commands (for both
3520c0d06caSMauro Carvalho Chehab 	   24xxx and 29xxx devices).  Meanings are not entirely clear,
3530c0d06caSMauro Carvalho Chehab 	   however without the ENC_MISC(3,1) command then we risk
3540c0d06caSMauro Carvalho Chehab 	   random perpetual video corruption whenever the video input
3550c0d06caSMauro Carvalho Chehab 	   breaks up for a moment (like when switching channels). */
3560c0d06caSMauro Carvalho Chehab 
3570c0d06caSMauro Carvalho Chehab 
3580c0d06caSMauro Carvalho Chehab #if 0
3590c0d06caSMauro Carvalho Chehab 	/* This ENC_MISC(5,0) command seems to hurt 29xxx sync
3600c0d06caSMauro Carvalho Chehab 	   performance on channel changes, but is not a problem on
3610c0d06caSMauro Carvalho Chehab 	   24xxx devices. */
3620c0d06caSMauro Carvalho Chehab 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0);
3630c0d06caSMauro Carvalho Chehab #endif
3640c0d06caSMauro Carvalho Chehab 
3650c0d06caSMauro Carvalho Chehab 	/* This ENC_MISC(3,encMisc3Arg) command is critical - without
3660c0d06caSMauro Carvalho Chehab 	   it there will eventually be video corruption.  Also, the
3670c0d06caSMauro Carvalho Chehab 	   saa7115 case is strange - the Windows driver is passing 1
3680c0d06caSMauro Carvalho Chehab 	   regardless of device type but if we have 1 for saa7115
3690c0d06caSMauro Carvalho Chehab 	   devices the video turns sluggish.  */
3700c0d06caSMauro Carvalho Chehab 	if (hdw->hdw_desc->flag_has_cx25840) {
3710c0d06caSMauro Carvalho Chehab 		encMisc3Arg = 1;
3720c0d06caSMauro Carvalho Chehab 	} else {
3730c0d06caSMauro Carvalho Chehab 		encMisc3Arg = 0;
3740c0d06caSMauro Carvalho Chehab 	}
3750c0d06caSMauro Carvalho Chehab 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,
3760c0d06caSMauro Carvalho Chehab 				 encMisc3Arg,0,0);
3770c0d06caSMauro Carvalho Chehab 
3780c0d06caSMauro Carvalho Chehab 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0);
3790c0d06caSMauro Carvalho Chehab 
3800c0d06caSMauro Carvalho Chehab #if 0
3810c0d06caSMauro Carvalho Chehab 	/* This ENC_MISC(4,1) command is poisonous, so it is commented
3820c0d06caSMauro Carvalho Chehab 	   out.  But I'm leaving it here anyway to document its
3830c0d06caSMauro Carvalho Chehab 	   existence in the Windows driver.  The effect of this
3840c0d06caSMauro Carvalho Chehab 	   command is that apps displaying the stream become sluggish
3850c0d06caSMauro Carvalho Chehab 	   with stuttering video. */
3860c0d06caSMauro Carvalho Chehab 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0);
3870c0d06caSMauro Carvalho Chehab #endif
3880c0d06caSMauro Carvalho Chehab 
3890c0d06caSMauro Carvalho Chehab 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0);
3900c0d06caSMauro Carvalho Chehab 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0);
3910c0d06caSMauro Carvalho Chehab 
3920c0d06caSMauro Carvalho Chehab 	/* prevent the PTSs from slowly drifting away in the generated
3930c0d06caSMauro Carvalho Chehab 	   MPEG stream */
3940c0d06caSMauro Carvalho Chehab 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1);
3950c0d06caSMauro Carvalho Chehab 
3960c0d06caSMauro Carvalho Chehab 	return ret;
3970c0d06caSMauro Carvalho Chehab }
3980c0d06caSMauro Carvalho Chehab 
3990c0d06caSMauro Carvalho Chehab int pvr2_encoder_adjust(struct pvr2_hdw *hdw)
4000c0d06caSMauro Carvalho Chehab {
4010c0d06caSMauro Carvalho Chehab 	int ret;
4020c0d06caSMauro Carvalho Chehab 	ret = cx2341x_update(hdw,pvr2_encoder_cmd,
4030c0d06caSMauro Carvalho Chehab 			     (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
4040c0d06caSMauro Carvalho Chehab 			     &hdw->enc_ctl_state);
4050c0d06caSMauro Carvalho Chehab 	if (ret) {
4060c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
4070c0d06caSMauro Carvalho Chehab 			   "Error from cx2341x module code=%d",ret);
4080c0d06caSMauro Carvalho Chehab 	} else {
4095338c169SEzequiel Garcia 		hdw->enc_cur_state = hdw->enc_ctl_state;
4100c0d06caSMauro Carvalho Chehab 		hdw->enc_cur_valid = !0;
4110c0d06caSMauro Carvalho Chehab 	}
4120c0d06caSMauro Carvalho Chehab 	return ret;
4130c0d06caSMauro Carvalho Chehab }
4140c0d06caSMauro Carvalho Chehab 
4150c0d06caSMauro Carvalho Chehab 
4160c0d06caSMauro Carvalho Chehab int pvr2_encoder_configure(struct pvr2_hdw *hdw)
4170c0d06caSMauro Carvalho Chehab {
4180c0d06caSMauro Carvalho Chehab 	int ret;
4190c0d06caSMauro Carvalho Chehab 	int val;
42096292c89SMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_ENCODER, "pvr2_encoder_configure (cx2341x module)");
4210c0d06caSMauro Carvalho Chehab 	hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING;
4220c0d06caSMauro Carvalho Chehab 	hdw->enc_ctl_state.width = hdw->res_hor_val;
4230c0d06caSMauro Carvalho Chehab 	hdw->enc_ctl_state.height = hdw->res_ver_val;
4240c0d06caSMauro Carvalho Chehab 	hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ?
4250c0d06caSMauro Carvalho Chehab 				      0 : 1);
4260c0d06caSMauro Carvalho Chehab 
4270c0d06caSMauro Carvalho Chehab 	ret = 0;
4280c0d06caSMauro Carvalho Chehab 
4290c0d06caSMauro Carvalho Chehab 	ret |= pvr2_encoder_prep_config(hdw);
4300c0d06caSMauro Carvalho Chehab 
4310c0d06caSMauro Carvalho Chehab 	/* saa7115: 0xf0 */
4320c0d06caSMauro Carvalho Chehab 	val = 0xf0;
4330c0d06caSMauro Carvalho Chehab 	if (hdw->hdw_desc->flag_has_cx25840) {
4340c0d06caSMauro Carvalho Chehab 		/* ivtv cx25840: 0x140 */
4350c0d06caSMauro Carvalho Chehab 		val = 0x140;
4360c0d06caSMauro Carvalho Chehab 	}
4370c0d06caSMauro Carvalho Chehab 
4380c0d06caSMauro Carvalho Chehab 	if (!ret) ret = pvr2_encoder_vcmd(
4390c0d06caSMauro Carvalho Chehab 		hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
4400c0d06caSMauro Carvalho Chehab 		val, val);
4410c0d06caSMauro Carvalho Chehab 
4420c0d06caSMauro Carvalho Chehab 	/* setup firmware to notify us about some events (don't know why...) */
4430c0d06caSMauro Carvalho Chehab 	if (!ret) ret = pvr2_encoder_vcmd(
4440c0d06caSMauro Carvalho Chehab 		hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
4450c0d06caSMauro Carvalho Chehab 		0, 0, 0x10000000, 0xffffffff);
4460c0d06caSMauro Carvalho Chehab 
4470c0d06caSMauro Carvalho Chehab 	if (!ret) ret = pvr2_encoder_vcmd(
4480c0d06caSMauro Carvalho Chehab 		hdw,CX2341X_ENC_SET_VBI_LINE, 5,
4490c0d06caSMauro Carvalho Chehab 		0xffffffff,0,0,0,0);
4500c0d06caSMauro Carvalho Chehab 
4510c0d06caSMauro Carvalho Chehab 	if (ret) {
4520c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
4530c0d06caSMauro Carvalho Chehab 			   "Failed to configure cx23416");
4540c0d06caSMauro Carvalho Chehab 		return ret;
4550c0d06caSMauro Carvalho Chehab 	}
4560c0d06caSMauro Carvalho Chehab 
4570c0d06caSMauro Carvalho Chehab 	ret = pvr2_encoder_adjust(hdw);
4580c0d06caSMauro Carvalho Chehab 	if (ret) return ret;
4590c0d06caSMauro Carvalho Chehab 
4600c0d06caSMauro Carvalho Chehab 	ret = pvr2_encoder_vcmd(
4610c0d06caSMauro Carvalho Chehab 		hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
4620c0d06caSMauro Carvalho Chehab 
4630c0d06caSMauro Carvalho Chehab 	if (ret) {
4640c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
4650c0d06caSMauro Carvalho Chehab 			   "Failed to initialize cx23416 video input");
4660c0d06caSMauro Carvalho Chehab 		return ret;
4670c0d06caSMauro Carvalho Chehab 	}
4680c0d06caSMauro Carvalho Chehab 
4690c0d06caSMauro Carvalho Chehab 	return 0;
4700c0d06caSMauro Carvalho Chehab }
4710c0d06caSMauro Carvalho Chehab 
4720c0d06caSMauro Carvalho Chehab 
4730c0d06caSMauro Carvalho Chehab int pvr2_encoder_start(struct pvr2_hdw *hdw)
4740c0d06caSMauro Carvalho Chehab {
4750c0d06caSMauro Carvalho Chehab 	int status;
4760c0d06caSMauro Carvalho Chehab 
4770c0d06caSMauro Carvalho Chehab 	/* unmask some interrupts */
4780c0d06caSMauro Carvalho Chehab 	pvr2_write_register(hdw, 0x0048, 0xbfffffff);
4790c0d06caSMauro Carvalho Chehab 
4800c0d06caSMauro Carvalho Chehab 	pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
4810c0d06caSMauro Carvalho Chehab 			  hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
4820c0d06caSMauro Carvalho Chehab 
4830c0d06caSMauro Carvalho Chehab 	switch (hdw->active_stream_type) {
4840c0d06caSMauro Carvalho Chehab 	case pvr2_config_vbi:
4850c0d06caSMauro Carvalho Chehab 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
4860c0d06caSMauro Carvalho Chehab 					   0x01,0x14);
4870c0d06caSMauro Carvalho Chehab 		break;
4880c0d06caSMauro Carvalho Chehab 	case pvr2_config_mpeg:
4890c0d06caSMauro Carvalho Chehab 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
4900c0d06caSMauro Carvalho Chehab 					   0,0x13);
4910c0d06caSMauro Carvalho Chehab 		break;
4920c0d06caSMauro Carvalho Chehab 	default: /* Unhandled cases for now */
4930c0d06caSMauro Carvalho Chehab 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
4940c0d06caSMauro Carvalho Chehab 					   0,0x13);
4950c0d06caSMauro Carvalho Chehab 		break;
4960c0d06caSMauro Carvalho Chehab 	}
4970c0d06caSMauro Carvalho Chehab 	return status;
4980c0d06caSMauro Carvalho Chehab }
4990c0d06caSMauro Carvalho Chehab 
5000c0d06caSMauro Carvalho Chehab int pvr2_encoder_stop(struct pvr2_hdw *hdw)
5010c0d06caSMauro Carvalho Chehab {
5020c0d06caSMauro Carvalho Chehab 	int status;
5030c0d06caSMauro Carvalho Chehab 
5040c0d06caSMauro Carvalho Chehab 	/* mask all interrupts */
5050c0d06caSMauro Carvalho Chehab 	pvr2_write_register(hdw, 0x0048, 0xffffffff);
5060c0d06caSMauro Carvalho Chehab 
5070c0d06caSMauro Carvalho Chehab 	switch (hdw->active_stream_type) {
5080c0d06caSMauro Carvalho Chehab 	case pvr2_config_vbi:
5090c0d06caSMauro Carvalho Chehab 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
5100c0d06caSMauro Carvalho Chehab 					   0x01,0x01,0x14);
5110c0d06caSMauro Carvalho Chehab 		break;
5120c0d06caSMauro Carvalho Chehab 	case pvr2_config_mpeg:
5130c0d06caSMauro Carvalho Chehab 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
5140c0d06caSMauro Carvalho Chehab 					   0x01,0,0x13);
5150c0d06caSMauro Carvalho Chehab 		break;
5160c0d06caSMauro Carvalho Chehab 	default: /* Unhandled cases for now */
5170c0d06caSMauro Carvalho Chehab 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
5180c0d06caSMauro Carvalho Chehab 					   0x01,0,0x13);
5190c0d06caSMauro Carvalho Chehab 		break;
5200c0d06caSMauro Carvalho Chehab 	}
5210c0d06caSMauro Carvalho Chehab 
5220c0d06caSMauro Carvalho Chehab 	return status;
5230c0d06caSMauro Carvalho Chehab }
524