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