18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
58c2ecf20Sopenharmony_ci *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/device.h>   // for linux/firmware.h
98c2ecf20Sopenharmony_ci#include <linux/firmware.h>
108c2ecf20Sopenharmony_ci#include "pvrusb2-util.h"
118c2ecf20Sopenharmony_ci#include "pvrusb2-encoder.h"
128c2ecf20Sopenharmony_ci#include "pvrusb2-hdw-internal.h"
138c2ecf20Sopenharmony_ci#include "pvrusb2-debug.h"
148c2ecf20Sopenharmony_ci#include "pvrusb2-fx2-cmd.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* Firmware mailbox flags - definitions found from ivtv */
198c2ecf20Sopenharmony_ci#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
208c2ecf20Sopenharmony_ci#define IVTV_MBOX_DRIVER_DONE 0x00000002
218c2ecf20Sopenharmony_ci#define IVTV_MBOX_DRIVER_BUSY 0x00000001
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define MBOX_BASE 0x44
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
278c2ecf20Sopenharmony_ci				    unsigned int offs,
288c2ecf20Sopenharmony_ci				    const u32 *data, unsigned int dlen)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	unsigned int idx,addr;
318c2ecf20Sopenharmony_ci	unsigned int bAddr;
328c2ecf20Sopenharmony_ci	int ret;
338c2ecf20Sopenharmony_ci	unsigned int chunkCnt;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	/*
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	Format: First byte must be 0x01.  Remaining 32 bit words are
388c2ecf20Sopenharmony_ci	spread out into chunks of 7 bytes each, with the first 4 bytes
398c2ecf20Sopenharmony_ci	being the data word (little endian), and the next 3 bytes
408c2ecf20Sopenharmony_ci	being the address where that data word is to be written (big
418c2ecf20Sopenharmony_ci	endian).  Repeat request for additional words, with offset
428c2ecf20Sopenharmony_ci	adjusted accordingly.
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	*/
458c2ecf20Sopenharmony_ci	while (dlen) {
468c2ecf20Sopenharmony_ci		chunkCnt = 8;
478c2ecf20Sopenharmony_ci		if (chunkCnt > dlen) chunkCnt = dlen;
488c2ecf20Sopenharmony_ci		memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
498c2ecf20Sopenharmony_ci		bAddr = 0;
508c2ecf20Sopenharmony_ci		hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD;
518c2ecf20Sopenharmony_ci		for (idx = 0; idx < chunkCnt; idx++) {
528c2ecf20Sopenharmony_ci			addr = idx + offs;
538c2ecf20Sopenharmony_ci			hdw->cmd_buffer[bAddr+6] = (addr & 0xffu);
548c2ecf20Sopenharmony_ci			hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu);
558c2ecf20Sopenharmony_ci			hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu);
568c2ecf20Sopenharmony_ci			PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]);
578c2ecf20Sopenharmony_ci			bAddr += 7;
588c2ecf20Sopenharmony_ci		}
598c2ecf20Sopenharmony_ci		ret = pvr2_send_request(hdw,
608c2ecf20Sopenharmony_ci					hdw->cmd_buffer,1+(chunkCnt*7),
618c2ecf20Sopenharmony_ci					NULL,0);
628c2ecf20Sopenharmony_ci		if (ret) return ret;
638c2ecf20Sopenharmony_ci		data += chunkCnt;
648c2ecf20Sopenharmony_ci		dlen -= chunkCnt;
658c2ecf20Sopenharmony_ci		offs += chunkCnt;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int pvr2_encoder_read_words(struct pvr2_hdw *hdw,
738c2ecf20Sopenharmony_ci				   unsigned int offs,
748c2ecf20Sopenharmony_ci				   u32 *data, unsigned int dlen)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	unsigned int idx;
778c2ecf20Sopenharmony_ci	int ret;
788c2ecf20Sopenharmony_ci	unsigned int chunkCnt;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/*
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	Format: First byte must be 0x02 (status check) or 0x28 (read
838c2ecf20Sopenharmony_ci	back block of 32 bit words).  Next 6 bytes must be zero,
848c2ecf20Sopenharmony_ci	followed by a single byte of MBOX_BASE+offset for portion to
858c2ecf20Sopenharmony_ci	be read.  Returned data is packed set of 32 bits words that
868c2ecf20Sopenharmony_ci	were read.
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	*/
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	while (dlen) {
918c2ecf20Sopenharmony_ci		chunkCnt = 16;
928c2ecf20Sopenharmony_ci		if (chunkCnt > dlen) chunkCnt = dlen;
938c2ecf20Sopenharmony_ci		if (chunkCnt < 16) chunkCnt = 1;
948c2ecf20Sopenharmony_ci		hdw->cmd_buffer[0] =
958c2ecf20Sopenharmony_ci			((chunkCnt == 1) ?
968c2ecf20Sopenharmony_ci			 FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES);
978c2ecf20Sopenharmony_ci		hdw->cmd_buffer[1] = 0;
988c2ecf20Sopenharmony_ci		hdw->cmd_buffer[2] = 0;
998c2ecf20Sopenharmony_ci		hdw->cmd_buffer[3] = 0;
1008c2ecf20Sopenharmony_ci		hdw->cmd_buffer[4] = 0;
1018c2ecf20Sopenharmony_ci		hdw->cmd_buffer[5] = ((offs>>16) & 0xffu);
1028c2ecf20Sopenharmony_ci		hdw->cmd_buffer[6] = ((offs>>8) & 0xffu);
1038c2ecf20Sopenharmony_ci		hdw->cmd_buffer[7] = (offs & 0xffu);
1048c2ecf20Sopenharmony_ci		ret = pvr2_send_request(hdw,
1058c2ecf20Sopenharmony_ci					hdw->cmd_buffer,8,
1068c2ecf20Sopenharmony_ci					hdw->cmd_buffer,
1078c2ecf20Sopenharmony_ci					(chunkCnt == 1 ? 4 : 16 * 4));
1088c2ecf20Sopenharmony_ci		if (ret) return ret;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		for (idx = 0; idx < chunkCnt; idx++) {
1118c2ecf20Sopenharmony_ci			data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4);
1128c2ecf20Sopenharmony_ci		}
1138c2ecf20Sopenharmony_ci		data += chunkCnt;
1148c2ecf20Sopenharmony_ci		dlen -= chunkCnt;
1158c2ecf20Sopenharmony_ci		offs += chunkCnt;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return 0;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/* This prototype is set up to be compatible with the
1238c2ecf20Sopenharmony_ci   cx2341x_mbox_func prototype in cx2341x.h, which should be in
1248c2ecf20Sopenharmony_ci   kernels 2.6.18 or later.  We do this so that we can enable
1258c2ecf20Sopenharmony_ci   cx2341x.ko to write to our encoder (by handing it a pointer to this
1268c2ecf20Sopenharmony_ci   function).  For earlier kernels this doesn't really matter. */
1278c2ecf20Sopenharmony_cistatic int pvr2_encoder_cmd(void *ctxt,
1288c2ecf20Sopenharmony_ci			    u32 cmd,
1298c2ecf20Sopenharmony_ci			    int arg_cnt_send,
1308c2ecf20Sopenharmony_ci			    int arg_cnt_recv,
1318c2ecf20Sopenharmony_ci			    u32 *argp)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	unsigned int poll_count;
1348c2ecf20Sopenharmony_ci	unsigned int try_count = 0;
1358c2ecf20Sopenharmony_ci	int retry_flag;
1368c2ecf20Sopenharmony_ci	int ret = 0;
1378c2ecf20Sopenharmony_ci	unsigned int idx;
1388c2ecf20Sopenharmony_ci	/* These sizes look to be limited by the FX2 firmware implementation */
1398c2ecf20Sopenharmony_ci	u32 wrData[16];
1408c2ecf20Sopenharmony_ci	u32 rdData[16];
1418c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/*
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	The encoder seems to speak entirely using blocks 32 bit words.
1478c2ecf20Sopenharmony_ci	In ivtv driver terms, this is a mailbox at MBOX_BASE which we
1488c2ecf20Sopenharmony_ci	populate with data and watch what the hardware does with it.
1498c2ecf20Sopenharmony_ci	The first word is a set of flags used to control the
1508c2ecf20Sopenharmony_ci	transaction, the second word is the command to execute, the
1518c2ecf20Sopenharmony_ci	third byte is zero (ivtv driver suggests that this is some
1528c2ecf20Sopenharmony_ci	kind of return value), and the fourth byte is a specified
1538c2ecf20Sopenharmony_ci	timeout (windows driver always uses 0x00060000 except for one
1548c2ecf20Sopenharmony_ci	case when it is zero).  All successive words are the argument
1558c2ecf20Sopenharmony_ci	words for the command.
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	First, write out the entire set of words, with the first word
1588c2ecf20Sopenharmony_ci	being zero.
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	Next, write out just the first word again, but set it to
1618c2ecf20Sopenharmony_ci	IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which
1628c2ecf20Sopenharmony_ci	probably means "go").
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	Next, read back the return count words.  Check the first word,
1658c2ecf20Sopenharmony_ci	which should have IVTV_MBOX_FIRMWARE_DONE set.  If however
1668c2ecf20Sopenharmony_ci	that bit is not set, then the command isn't done so repeat the
1678c2ecf20Sopenharmony_ci	read until it is set.
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	Finally, write out just the first word again, but set it to
1708c2ecf20Sopenharmony_ci	0x0 this time (which probably means "idle").
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	*/
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) {
1758c2ecf20Sopenharmony_ci		pvr2_trace(
1768c2ecf20Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
1778c2ecf20Sopenharmony_ci			"Failed to write cx23416 command - too many input arguments (was given %u limit %lu)",
1788c2ecf20Sopenharmony_ci			arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4);
1798c2ecf20Sopenharmony_ci		return -EINVAL;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) {
1838c2ecf20Sopenharmony_ci		pvr2_trace(
1848c2ecf20Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
1858c2ecf20Sopenharmony_ci			"Failed to write cx23416 command - too many return arguments (was given %u limit %lu)",
1868c2ecf20Sopenharmony_ci			arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4);
1878c2ecf20Sopenharmony_ci		return -EINVAL;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock); while (1) {
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		if (!hdw->state_encoder_ok) {
1948c2ecf20Sopenharmony_ci			ret = -EIO;
1958c2ecf20Sopenharmony_ci			break;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		retry_flag = 0;
1998c2ecf20Sopenharmony_ci		try_count++;
2008c2ecf20Sopenharmony_ci		ret = 0;
2018c2ecf20Sopenharmony_ci		wrData[0] = 0;
2028c2ecf20Sopenharmony_ci		wrData[1] = cmd;
2038c2ecf20Sopenharmony_ci		wrData[2] = 0;
2048c2ecf20Sopenharmony_ci		wrData[3] = 0x00060000;
2058c2ecf20Sopenharmony_ci		for (idx = 0; idx < arg_cnt_send; idx++) {
2068c2ecf20Sopenharmony_ci			wrData[idx+4] = argp[idx];
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci		for (; idx < ARRAY_SIZE(wrData) - 4; idx++) {
2098c2ecf20Sopenharmony_ci			wrData[idx+4] = 0;
2108c2ecf20Sopenharmony_ci		}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx);
2138c2ecf20Sopenharmony_ci		if (ret) break;
2148c2ecf20Sopenharmony_ci		wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
2158c2ecf20Sopenharmony_ci		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
2168c2ecf20Sopenharmony_ci		if (ret) break;
2178c2ecf20Sopenharmony_ci		poll_count = 0;
2188c2ecf20Sopenharmony_ci		while (1) {
2198c2ecf20Sopenharmony_ci			poll_count++;
2208c2ecf20Sopenharmony_ci			ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData,
2218c2ecf20Sopenharmony_ci						      arg_cnt_recv+4);
2228c2ecf20Sopenharmony_ci			if (ret) {
2238c2ecf20Sopenharmony_ci				break;
2248c2ecf20Sopenharmony_ci			}
2258c2ecf20Sopenharmony_ci			if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
2268c2ecf20Sopenharmony_ci				break;
2278c2ecf20Sopenharmony_ci			}
2288c2ecf20Sopenharmony_ci			if (rdData[0] && (poll_count < 1000)) continue;
2298c2ecf20Sopenharmony_ci			if (!rdData[0]) {
2308c2ecf20Sopenharmony_ci				retry_flag = !0;
2318c2ecf20Sopenharmony_ci				pvr2_trace(
2328c2ecf20Sopenharmony_ci					PVR2_TRACE_ERROR_LEGS,
2338c2ecf20Sopenharmony_ci					"Encoder timed out waiting for us; arranging to retry");
2348c2ecf20Sopenharmony_ci			} else {
2358c2ecf20Sopenharmony_ci				pvr2_trace(
2368c2ecf20Sopenharmony_ci					PVR2_TRACE_ERROR_LEGS,
2378c2ecf20Sopenharmony_ci					"***WARNING*** device's encoder appears to be stuck (status=0x%08x)",
2388c2ecf20Sopenharmony_cirdData[0]);
2398c2ecf20Sopenharmony_ci			}
2408c2ecf20Sopenharmony_ci			pvr2_trace(
2418c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
2428c2ecf20Sopenharmony_ci				"Encoder command: 0x%02x",cmd);
2438c2ecf20Sopenharmony_ci			for (idx = 4; idx < arg_cnt_send; idx++) {
2448c2ecf20Sopenharmony_ci				pvr2_trace(
2458c2ecf20Sopenharmony_ci					PVR2_TRACE_ERROR_LEGS,
2468c2ecf20Sopenharmony_ci					"Encoder arg%d: 0x%08x",
2478c2ecf20Sopenharmony_ci					idx-3,wrData[idx]);
2488c2ecf20Sopenharmony_ci			}
2498c2ecf20Sopenharmony_ci			ret = -EBUSY;
2508c2ecf20Sopenharmony_ci			break;
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci		if (retry_flag) {
2538c2ecf20Sopenharmony_ci			if (try_count < 20) continue;
2548c2ecf20Sopenharmony_ci			pvr2_trace(
2558c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
2568c2ecf20Sopenharmony_ci				"Too many retries...");
2578c2ecf20Sopenharmony_ci			ret = -EBUSY;
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci		if (ret) {
2608c2ecf20Sopenharmony_ci			del_timer_sync(&hdw->encoder_run_timer);
2618c2ecf20Sopenharmony_ci			hdw->state_encoder_ok = 0;
2628c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_STBITS,
2638c2ecf20Sopenharmony_ci				   "State bit %s <-- %s",
2648c2ecf20Sopenharmony_ci				   "state_encoder_ok",
2658c2ecf20Sopenharmony_ci				   (hdw->state_encoder_ok ? "true" : "false"));
2668c2ecf20Sopenharmony_ci			if (hdw->state_encoder_runok) {
2678c2ecf20Sopenharmony_ci				hdw->state_encoder_runok = 0;
2688c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_STBITS,
2698c2ecf20Sopenharmony_ci				   "State bit %s <-- %s",
2708c2ecf20Sopenharmony_ci					   "state_encoder_runok",
2718c2ecf20Sopenharmony_ci					   (hdw->state_encoder_runok ?
2728c2ecf20Sopenharmony_ci					    "true" : "false"));
2738c2ecf20Sopenharmony_ci			}
2748c2ecf20Sopenharmony_ci			pvr2_trace(
2758c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
2768c2ecf20Sopenharmony_ci				"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.");
2778c2ecf20Sopenharmony_ci			break;
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci		wrData[0] = 0x7;
2808c2ecf20Sopenharmony_ci		for (idx = 0; idx < arg_cnt_recv; idx++) {
2818c2ecf20Sopenharmony_ci			argp[idx] = rdData[idx+4];
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		wrData[0] = 0x0;
2858c2ecf20Sopenharmony_ci		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
2868c2ecf20Sopenharmony_ci		break;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->ctl_lock);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return ret;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd,
2958c2ecf20Sopenharmony_ci			     int args, ...)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	va_list vl;
2988c2ecf20Sopenharmony_ci	unsigned int idx;
2998c2ecf20Sopenharmony_ci	u32 data[12];
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (args > ARRAY_SIZE(data)) {
3028c2ecf20Sopenharmony_ci		pvr2_trace(
3038c2ecf20Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
3048c2ecf20Sopenharmony_ci			"Failed to write cx23416 command - too many arguments (was given %u limit %lu)",
3058c2ecf20Sopenharmony_ci			args, (long unsigned) ARRAY_SIZE(data));
3068c2ecf20Sopenharmony_ci		return -EINVAL;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	va_start(vl, args);
3108c2ecf20Sopenharmony_ci	for (idx = 0; idx < args; idx++) {
3118c2ecf20Sopenharmony_ci		data[idx] = va_arg(vl, u32);
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci	va_end(vl);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return pvr2_encoder_cmd(hdw,cmd,args,0,data);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/* This implements some extra setup for the encoder that seems to be
3208c2ecf20Sopenharmony_ci   specific to the PVR USB2 hardware. */
3218c2ecf20Sopenharmony_cistatic int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	int ret = 0;
3248c2ecf20Sopenharmony_ci	int encMisc3Arg = 0;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci#if 0
3278c2ecf20Sopenharmony_ci	/* This inexplicable bit happens in the Hauppauge windows
3288c2ecf20Sopenharmony_ci	   driver (for both 24xxx and 29xxx devices).  However I
3298c2ecf20Sopenharmony_ci	   currently see no difference in behavior with or without
3308c2ecf20Sopenharmony_ci	   this stuff.  Leave this here as a note of its existence,
3318c2ecf20Sopenharmony_ci	   but don't use it. */
3328c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock); do {
3338c2ecf20Sopenharmony_ci		u32 dat[1];
3348c2ecf20Sopenharmony_ci		dat[0] = 0x80000640;
3358c2ecf20Sopenharmony_ci		pvr2_encoder_write_words(hdw,0x01fe,dat,1);
3368c2ecf20Sopenharmony_ci		pvr2_encoder_write_words(hdw,0x023e,dat,1);
3378c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(hdw->ctl_lock);
3388c2ecf20Sopenharmony_ci#endif
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver
3418c2ecf20Sopenharmony_ci	   sends the following list of ENC_MISC commands (for both
3428c2ecf20Sopenharmony_ci	   24xxx and 29xxx devices).  Meanings are not entirely clear,
3438c2ecf20Sopenharmony_ci	   however without the ENC_MISC(3,1) command then we risk
3448c2ecf20Sopenharmony_ci	   random perpetual video corruption whenever the video input
3458c2ecf20Sopenharmony_ci	   breaks up for a moment (like when switching channels). */
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci#if 0
3498c2ecf20Sopenharmony_ci	/* This ENC_MISC(5,0) command seems to hurt 29xxx sync
3508c2ecf20Sopenharmony_ci	   performance on channel changes, but is not a problem on
3518c2ecf20Sopenharmony_ci	   24xxx devices. */
3528c2ecf20Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0);
3538c2ecf20Sopenharmony_ci#endif
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* This ENC_MISC(3,encMisc3Arg) command is critical - without
3568c2ecf20Sopenharmony_ci	   it there will eventually be video corruption.  Also, the
3578c2ecf20Sopenharmony_ci	   saa7115 case is strange - the Windows driver is passing 1
3588c2ecf20Sopenharmony_ci	   regardless of device type but if we have 1 for saa7115
3598c2ecf20Sopenharmony_ci	   devices the video turns sluggish.  */
3608c2ecf20Sopenharmony_ci	if (hdw->hdw_desc->flag_has_cx25840) {
3618c2ecf20Sopenharmony_ci		encMisc3Arg = 1;
3628c2ecf20Sopenharmony_ci	} else {
3638c2ecf20Sopenharmony_ci		encMisc3Arg = 0;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,
3668c2ecf20Sopenharmony_ci				 encMisc3Arg,0,0);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci#if 0
3718c2ecf20Sopenharmony_ci	/* This ENC_MISC(4,1) command is poisonous, so it is commented
3728c2ecf20Sopenharmony_ci	   out.  But I'm leaving it here anyway to document its
3738c2ecf20Sopenharmony_ci	   existence in the Windows driver.  The effect of this
3748c2ecf20Sopenharmony_ci	   command is that apps displaying the stream become sluggish
3758c2ecf20Sopenharmony_ci	   with stuttering video. */
3768c2ecf20Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0);
3778c2ecf20Sopenharmony_ci#endif
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0);
3808c2ecf20Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/* prevent the PTSs from slowly drifting away in the generated
3838c2ecf20Sopenharmony_ci	   MPEG stream */
3848c2ecf20Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return ret;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ciint pvr2_encoder_adjust(struct pvr2_hdw *hdw)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	int ret;
3928c2ecf20Sopenharmony_ci	ret = cx2341x_update(hdw,pvr2_encoder_cmd,
3938c2ecf20Sopenharmony_ci			     (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
3948c2ecf20Sopenharmony_ci			     &hdw->enc_ctl_state);
3958c2ecf20Sopenharmony_ci	if (ret) {
3968c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
3978c2ecf20Sopenharmony_ci			   "Error from cx2341x module code=%d",ret);
3988c2ecf20Sopenharmony_ci	} else {
3998c2ecf20Sopenharmony_ci		hdw->enc_cur_state = hdw->enc_ctl_state;
4008c2ecf20Sopenharmony_ci		hdw->enc_cur_valid = !0;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci	return ret;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ciint pvr2_encoder_configure(struct pvr2_hdw *hdw)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	int ret;
4098c2ecf20Sopenharmony_ci	int val;
4108c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_ENCODER, "pvr2_encoder_configure (cx2341x module)");
4118c2ecf20Sopenharmony_ci	hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING;
4128c2ecf20Sopenharmony_ci	hdw->enc_ctl_state.width = hdw->res_hor_val;
4138c2ecf20Sopenharmony_ci	hdw->enc_ctl_state.height = hdw->res_ver_val;
4148c2ecf20Sopenharmony_ci	hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ?
4158c2ecf20Sopenharmony_ci				      0 : 1);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	ret = 0;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	ret |= pvr2_encoder_prep_config(hdw);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* saa7115: 0xf0 */
4228c2ecf20Sopenharmony_ci	val = 0xf0;
4238c2ecf20Sopenharmony_ci	if (hdw->hdw_desc->flag_has_cx25840) {
4248c2ecf20Sopenharmony_ci		/* ivtv cx25840: 0x140 */
4258c2ecf20Sopenharmony_ci		val = 0x140;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (!ret) ret = pvr2_encoder_vcmd(
4298c2ecf20Sopenharmony_ci		hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
4308c2ecf20Sopenharmony_ci		val, val);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* setup firmware to notify us about some events (don't know why...) */
4338c2ecf20Sopenharmony_ci	if (!ret) ret = pvr2_encoder_vcmd(
4348c2ecf20Sopenharmony_ci		hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
4358c2ecf20Sopenharmony_ci		0, 0, 0x10000000, 0xffffffff);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (!ret) ret = pvr2_encoder_vcmd(
4388c2ecf20Sopenharmony_ci		hdw,CX2341X_ENC_SET_VBI_LINE, 5,
4398c2ecf20Sopenharmony_ci		0xffffffff,0,0,0,0);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (ret) {
4428c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
4438c2ecf20Sopenharmony_ci			   "Failed to configure cx23416");
4448c2ecf20Sopenharmony_ci		return ret;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	ret = pvr2_encoder_adjust(hdw);
4488c2ecf20Sopenharmony_ci	if (ret) return ret;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	ret = pvr2_encoder_vcmd(
4518c2ecf20Sopenharmony_ci		hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (ret) {
4548c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
4558c2ecf20Sopenharmony_ci			   "Failed to initialize cx23416 video input");
4568c2ecf20Sopenharmony_ci		return ret;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	return 0;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ciint pvr2_encoder_start(struct pvr2_hdw *hdw)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	int status;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	/* unmask some interrupts */
4688c2ecf20Sopenharmony_ci	pvr2_write_register(hdw, 0x0048, 0xbfffffff);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
4718c2ecf20Sopenharmony_ci			  hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	switch (hdw->active_stream_type) {
4748c2ecf20Sopenharmony_ci	case pvr2_config_vbi:
4758c2ecf20Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
4768c2ecf20Sopenharmony_ci					   0x01,0x14);
4778c2ecf20Sopenharmony_ci		break;
4788c2ecf20Sopenharmony_ci	case pvr2_config_mpeg:
4798c2ecf20Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
4808c2ecf20Sopenharmony_ci					   0,0x13);
4818c2ecf20Sopenharmony_ci		break;
4828c2ecf20Sopenharmony_ci	default: /* Unhandled cases for now */
4838c2ecf20Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
4848c2ecf20Sopenharmony_ci					   0,0x13);
4858c2ecf20Sopenharmony_ci		break;
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci	return status;
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ciint pvr2_encoder_stop(struct pvr2_hdw *hdw)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	int status;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/* mask all interrupts */
4958c2ecf20Sopenharmony_ci	pvr2_write_register(hdw, 0x0048, 0xffffffff);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	switch (hdw->active_stream_type) {
4988c2ecf20Sopenharmony_ci	case pvr2_config_vbi:
4998c2ecf20Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
5008c2ecf20Sopenharmony_ci					   0x01,0x01,0x14);
5018c2ecf20Sopenharmony_ci		break;
5028c2ecf20Sopenharmony_ci	case pvr2_config_mpeg:
5038c2ecf20Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
5048c2ecf20Sopenharmony_ci					   0x01,0,0x13);
5058c2ecf20Sopenharmony_ci		break;
5068c2ecf20Sopenharmony_ci	default: /* Unhandled cases for now */
5078c2ecf20Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
5088c2ecf20Sopenharmony_ci					   0x01,0,0x13);
5098c2ecf20Sopenharmony_ci		break;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	return status;
5138c2ecf20Sopenharmony_ci}
514