162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
562306a36Sopenharmony_ci *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/device.h>   // for linux/firmware.h
962306a36Sopenharmony_ci#include <linux/firmware.h>
1062306a36Sopenharmony_ci#include "pvrusb2-util.h"
1162306a36Sopenharmony_ci#include "pvrusb2-encoder.h"
1262306a36Sopenharmony_ci#include "pvrusb2-hdw-internal.h"
1362306a36Sopenharmony_ci#include "pvrusb2-debug.h"
1462306a36Sopenharmony_ci#include "pvrusb2-fx2-cmd.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* Firmware mailbox flags - definitions found from ivtv */
1962306a36Sopenharmony_ci#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
2062306a36Sopenharmony_ci#define IVTV_MBOX_DRIVER_DONE 0x00000002
2162306a36Sopenharmony_ci#define IVTV_MBOX_DRIVER_BUSY 0x00000001
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define MBOX_BASE 0x44
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
2762306a36Sopenharmony_ci				    unsigned int offs,
2862306a36Sopenharmony_ci				    const u32 *data, unsigned int dlen)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	unsigned int idx,addr;
3162306a36Sopenharmony_ci	unsigned int bAddr;
3262306a36Sopenharmony_ci	int ret;
3362306a36Sopenharmony_ci	unsigned int chunkCnt;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/*
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	Format: First byte must be 0x01.  Remaining 32 bit words are
3862306a36Sopenharmony_ci	spread out into chunks of 7 bytes each, with the first 4 bytes
3962306a36Sopenharmony_ci	being the data word (little endian), and the next 3 bytes
4062306a36Sopenharmony_ci	being the address where that data word is to be written (big
4162306a36Sopenharmony_ci	endian).  Repeat request for additional words, with offset
4262306a36Sopenharmony_ci	adjusted accordingly.
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	*/
4562306a36Sopenharmony_ci	while (dlen) {
4662306a36Sopenharmony_ci		chunkCnt = 8;
4762306a36Sopenharmony_ci		if (chunkCnt > dlen) chunkCnt = dlen;
4862306a36Sopenharmony_ci		memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
4962306a36Sopenharmony_ci		bAddr = 0;
5062306a36Sopenharmony_ci		hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD;
5162306a36Sopenharmony_ci		for (idx = 0; idx < chunkCnt; idx++) {
5262306a36Sopenharmony_ci			addr = idx + offs;
5362306a36Sopenharmony_ci			hdw->cmd_buffer[bAddr+6] = (addr & 0xffu);
5462306a36Sopenharmony_ci			hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu);
5562306a36Sopenharmony_ci			hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu);
5662306a36Sopenharmony_ci			PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]);
5762306a36Sopenharmony_ci			bAddr += 7;
5862306a36Sopenharmony_ci		}
5962306a36Sopenharmony_ci		ret = pvr2_send_request(hdw,
6062306a36Sopenharmony_ci					hdw->cmd_buffer,1+(chunkCnt*7),
6162306a36Sopenharmony_ci					NULL,0);
6262306a36Sopenharmony_ci		if (ret) return ret;
6362306a36Sopenharmony_ci		data += chunkCnt;
6462306a36Sopenharmony_ci		dlen -= chunkCnt;
6562306a36Sopenharmony_ci		offs += chunkCnt;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return 0;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int pvr2_encoder_read_words(struct pvr2_hdw *hdw,
7362306a36Sopenharmony_ci				   unsigned int offs,
7462306a36Sopenharmony_ci				   u32 *data, unsigned int dlen)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	unsigned int idx;
7762306a36Sopenharmony_ci	int ret;
7862306a36Sopenharmony_ci	unsigned int chunkCnt;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/*
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	Format: First byte must be 0x02 (status check) or 0x28 (read
8362306a36Sopenharmony_ci	back block of 32 bit words).  Next 6 bytes must be zero,
8462306a36Sopenharmony_ci	followed by a single byte of MBOX_BASE+offset for portion to
8562306a36Sopenharmony_ci	be read.  Returned data is packed set of 32 bits words that
8662306a36Sopenharmony_ci	were read.
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	*/
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	while (dlen) {
9162306a36Sopenharmony_ci		chunkCnt = 16;
9262306a36Sopenharmony_ci		if (chunkCnt > dlen) chunkCnt = dlen;
9362306a36Sopenharmony_ci		if (chunkCnt < 16) chunkCnt = 1;
9462306a36Sopenharmony_ci		hdw->cmd_buffer[0] =
9562306a36Sopenharmony_ci			((chunkCnt == 1) ?
9662306a36Sopenharmony_ci			 FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES);
9762306a36Sopenharmony_ci		hdw->cmd_buffer[1] = 0;
9862306a36Sopenharmony_ci		hdw->cmd_buffer[2] = 0;
9962306a36Sopenharmony_ci		hdw->cmd_buffer[3] = 0;
10062306a36Sopenharmony_ci		hdw->cmd_buffer[4] = 0;
10162306a36Sopenharmony_ci		hdw->cmd_buffer[5] = ((offs>>16) & 0xffu);
10262306a36Sopenharmony_ci		hdw->cmd_buffer[6] = ((offs>>8) & 0xffu);
10362306a36Sopenharmony_ci		hdw->cmd_buffer[7] = (offs & 0xffu);
10462306a36Sopenharmony_ci		ret = pvr2_send_request(hdw,
10562306a36Sopenharmony_ci					hdw->cmd_buffer,8,
10662306a36Sopenharmony_ci					hdw->cmd_buffer,
10762306a36Sopenharmony_ci					(chunkCnt == 1 ? 4 : 16 * 4));
10862306a36Sopenharmony_ci		if (ret) return ret;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		for (idx = 0; idx < chunkCnt; idx++) {
11162306a36Sopenharmony_ci			data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4);
11262306a36Sopenharmony_ci		}
11362306a36Sopenharmony_ci		data += chunkCnt;
11462306a36Sopenharmony_ci		dlen -= chunkCnt;
11562306a36Sopenharmony_ci		offs += chunkCnt;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return 0;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/* This prototype is set up to be compatible with the
12362306a36Sopenharmony_ci   cx2341x_mbox_func prototype in cx2341x.h, which should be in
12462306a36Sopenharmony_ci   kernels 2.6.18 or later.  We do this so that we can enable
12562306a36Sopenharmony_ci   cx2341x.ko to write to our encoder (by handing it a pointer to this
12662306a36Sopenharmony_ci   function).  For earlier kernels this doesn't really matter. */
12762306a36Sopenharmony_cistatic int pvr2_encoder_cmd(void *ctxt,
12862306a36Sopenharmony_ci			    u32 cmd,
12962306a36Sopenharmony_ci			    int arg_cnt_send,
13062306a36Sopenharmony_ci			    int arg_cnt_recv,
13162306a36Sopenharmony_ci			    u32 *argp)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	unsigned int poll_count;
13462306a36Sopenharmony_ci	unsigned int try_count = 0;
13562306a36Sopenharmony_ci	int retry_flag;
13662306a36Sopenharmony_ci	int ret = 0;
13762306a36Sopenharmony_ci	unsigned int idx;
13862306a36Sopenharmony_ci	/* These sizes look to be limited by the FX2 firmware implementation */
13962306a36Sopenharmony_ci	u32 wrData[16];
14062306a36Sopenharmony_ci	u32 rdData[16];
14162306a36Sopenharmony_ci	struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/*
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	The encoder seems to speak entirely using blocks 32 bit words.
14762306a36Sopenharmony_ci	In ivtv driver terms, this is a mailbox at MBOX_BASE which we
14862306a36Sopenharmony_ci	populate with data and watch what the hardware does with it.
14962306a36Sopenharmony_ci	The first word is a set of flags used to control the
15062306a36Sopenharmony_ci	transaction, the second word is the command to execute, the
15162306a36Sopenharmony_ci	third byte is zero (ivtv driver suggests that this is some
15262306a36Sopenharmony_ci	kind of return value), and the fourth byte is a specified
15362306a36Sopenharmony_ci	timeout (windows driver always uses 0x00060000 except for one
15462306a36Sopenharmony_ci	case when it is zero).  All successive words are the argument
15562306a36Sopenharmony_ci	words for the command.
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	First, write out the entire set of words, with the first word
15862306a36Sopenharmony_ci	being zero.
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	Next, write out just the first word again, but set it to
16162306a36Sopenharmony_ci	IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which
16262306a36Sopenharmony_ci	probably means "go").
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	Next, read back the return count words.  Check the first word,
16562306a36Sopenharmony_ci	which should have IVTV_MBOX_FIRMWARE_DONE set.  If however
16662306a36Sopenharmony_ci	that bit is not set, then the command isn't done so repeat the
16762306a36Sopenharmony_ci	read until it is set.
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	Finally, write out just the first word again, but set it to
17062306a36Sopenharmony_ci	0x0 this time (which probably means "idle").
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	*/
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) {
17562306a36Sopenharmony_ci		pvr2_trace(
17662306a36Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
17762306a36Sopenharmony_ci			"Failed to write cx23416 command - too many input arguments (was given %u limit %lu)",
17862306a36Sopenharmony_ci			arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4);
17962306a36Sopenharmony_ci		return -EINVAL;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) {
18362306a36Sopenharmony_ci		pvr2_trace(
18462306a36Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
18562306a36Sopenharmony_ci			"Failed to write cx23416 command - too many return arguments (was given %u limit %lu)",
18662306a36Sopenharmony_ci			arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4);
18762306a36Sopenharmony_ci		return -EINVAL;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock);
19262306a36Sopenharmony_ci	while (1) {
19362306a36Sopenharmony_ci		if (!hdw->state_encoder_ok) {
19462306a36Sopenharmony_ci			ret = -EIO;
19562306a36Sopenharmony_ci			break;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		retry_flag = 0;
19962306a36Sopenharmony_ci		try_count++;
20062306a36Sopenharmony_ci		ret = 0;
20162306a36Sopenharmony_ci		wrData[0] = 0;
20262306a36Sopenharmony_ci		wrData[1] = cmd;
20362306a36Sopenharmony_ci		wrData[2] = 0;
20462306a36Sopenharmony_ci		wrData[3] = 0x00060000;
20562306a36Sopenharmony_ci		for (idx = 0; idx < arg_cnt_send; idx++) {
20662306a36Sopenharmony_ci			wrData[idx+4] = argp[idx];
20762306a36Sopenharmony_ci		}
20862306a36Sopenharmony_ci		for (; idx < ARRAY_SIZE(wrData) - 4; idx++) {
20962306a36Sopenharmony_ci			wrData[idx+4] = 0;
21062306a36Sopenharmony_ci		}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx);
21362306a36Sopenharmony_ci		if (ret) break;
21462306a36Sopenharmony_ci		wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
21562306a36Sopenharmony_ci		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
21662306a36Sopenharmony_ci		if (ret) break;
21762306a36Sopenharmony_ci		poll_count = 0;
21862306a36Sopenharmony_ci		while (1) {
21962306a36Sopenharmony_ci			poll_count++;
22062306a36Sopenharmony_ci			ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData,
22162306a36Sopenharmony_ci						      arg_cnt_recv+4);
22262306a36Sopenharmony_ci			if (ret) {
22362306a36Sopenharmony_ci				break;
22462306a36Sopenharmony_ci			}
22562306a36Sopenharmony_ci			if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
22662306a36Sopenharmony_ci				break;
22762306a36Sopenharmony_ci			}
22862306a36Sopenharmony_ci			if (rdData[0] && (poll_count < 1000)) continue;
22962306a36Sopenharmony_ci			if (!rdData[0]) {
23062306a36Sopenharmony_ci				retry_flag = !0;
23162306a36Sopenharmony_ci				pvr2_trace(
23262306a36Sopenharmony_ci					PVR2_TRACE_ERROR_LEGS,
23362306a36Sopenharmony_ci					"Encoder timed out waiting for us; arranging to retry");
23462306a36Sopenharmony_ci			} else {
23562306a36Sopenharmony_ci				pvr2_trace(
23662306a36Sopenharmony_ci					PVR2_TRACE_ERROR_LEGS,
23762306a36Sopenharmony_ci					"***WARNING*** device's encoder appears to be stuck (status=0x%08x)",
23862306a36Sopenharmony_cirdData[0]);
23962306a36Sopenharmony_ci			}
24062306a36Sopenharmony_ci			pvr2_trace(
24162306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
24262306a36Sopenharmony_ci				"Encoder command: 0x%02x",cmd);
24362306a36Sopenharmony_ci			for (idx = 4; idx < arg_cnt_send; idx++) {
24462306a36Sopenharmony_ci				pvr2_trace(
24562306a36Sopenharmony_ci					PVR2_TRACE_ERROR_LEGS,
24662306a36Sopenharmony_ci					"Encoder arg%d: 0x%08x",
24762306a36Sopenharmony_ci					idx-3,wrData[idx]);
24862306a36Sopenharmony_ci			}
24962306a36Sopenharmony_ci			ret = -EBUSY;
25062306a36Sopenharmony_ci			break;
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci		if (retry_flag) {
25362306a36Sopenharmony_ci			if (try_count < 20) continue;
25462306a36Sopenharmony_ci			pvr2_trace(
25562306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
25662306a36Sopenharmony_ci				"Too many retries...");
25762306a36Sopenharmony_ci			ret = -EBUSY;
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci		if (ret) {
26062306a36Sopenharmony_ci			del_timer_sync(&hdw->encoder_run_timer);
26162306a36Sopenharmony_ci			hdw->state_encoder_ok = 0;
26262306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_STBITS,
26362306a36Sopenharmony_ci				   "State bit %s <-- %s",
26462306a36Sopenharmony_ci				   "state_encoder_ok",
26562306a36Sopenharmony_ci				   (hdw->state_encoder_ok ? "true" : "false"));
26662306a36Sopenharmony_ci			if (hdw->state_encoder_runok) {
26762306a36Sopenharmony_ci				hdw->state_encoder_runok = 0;
26862306a36Sopenharmony_ci				pvr2_trace(PVR2_TRACE_STBITS,
26962306a36Sopenharmony_ci				   "State bit %s <-- %s",
27062306a36Sopenharmony_ci					   "state_encoder_runok",
27162306a36Sopenharmony_ci					   (hdw->state_encoder_runok ?
27262306a36Sopenharmony_ci					    "true" : "false"));
27362306a36Sopenharmony_ci			}
27462306a36Sopenharmony_ci			pvr2_trace(
27562306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
27662306a36Sopenharmony_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.");
27762306a36Sopenharmony_ci			break;
27862306a36Sopenharmony_ci		}
27962306a36Sopenharmony_ci		wrData[0] = 0x7;
28062306a36Sopenharmony_ci		for (idx = 0; idx < arg_cnt_recv; idx++) {
28162306a36Sopenharmony_ci			argp[idx] = rdData[idx+4];
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		wrData[0] = 0x0;
28562306a36Sopenharmony_ci		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci	LOCK_GIVE(hdw->ctl_lock);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return ret;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd,
29562306a36Sopenharmony_ci			     int args, ...)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	va_list vl;
29862306a36Sopenharmony_ci	unsigned int idx;
29962306a36Sopenharmony_ci	u32 data[12];
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (args > ARRAY_SIZE(data)) {
30262306a36Sopenharmony_ci		pvr2_trace(
30362306a36Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
30462306a36Sopenharmony_ci			"Failed to write cx23416 command - too many arguments (was given %u limit %lu)",
30562306a36Sopenharmony_ci			args, (long unsigned) ARRAY_SIZE(data));
30662306a36Sopenharmony_ci		return -EINVAL;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	va_start(vl, args);
31062306a36Sopenharmony_ci	for (idx = 0; idx < args; idx++) {
31162306a36Sopenharmony_ci		data[idx] = va_arg(vl, u32);
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci	va_end(vl);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return pvr2_encoder_cmd(hdw,cmd,args,0,data);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci/* This implements some extra setup for the encoder that seems to be
32062306a36Sopenharmony_ci   specific to the PVR USB2 hardware. */
32162306a36Sopenharmony_cistatic int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	int ret = 0;
32462306a36Sopenharmony_ci	int encMisc3Arg = 0;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci#if 0
32762306a36Sopenharmony_ci	/* This inexplicable bit happens in the Hauppauge windows
32862306a36Sopenharmony_ci	   driver (for both 24xxx and 29xxx devices).  However I
32962306a36Sopenharmony_ci	   currently see no difference in behavior with or without
33062306a36Sopenharmony_ci	   this stuff.  Leave this here as a note of its existence,
33162306a36Sopenharmony_ci	   but don't use it. */
33262306a36Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock); do {
33362306a36Sopenharmony_ci		u32 dat[1];
33462306a36Sopenharmony_ci		dat[0] = 0x80000640;
33562306a36Sopenharmony_ci		pvr2_encoder_write_words(hdw,0x01fe,dat,1);
33662306a36Sopenharmony_ci		pvr2_encoder_write_words(hdw,0x023e,dat,1);
33762306a36Sopenharmony_ci	} while(0); LOCK_GIVE(hdw->ctl_lock);
33862306a36Sopenharmony_ci#endif
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver
34162306a36Sopenharmony_ci	   sends the following list of ENC_MISC commands (for both
34262306a36Sopenharmony_ci	   24xxx and 29xxx devices).  Meanings are not entirely clear,
34362306a36Sopenharmony_ci	   however without the ENC_MISC(3,1) command then we risk
34462306a36Sopenharmony_ci	   random perpetual video corruption whenever the video input
34562306a36Sopenharmony_ci	   breaks up for a moment (like when switching channels). */
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci#if 0
34962306a36Sopenharmony_ci	/* This ENC_MISC(5,0) command seems to hurt 29xxx sync
35062306a36Sopenharmony_ci	   performance on channel changes, but is not a problem on
35162306a36Sopenharmony_ci	   24xxx devices. */
35262306a36Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0);
35362306a36Sopenharmony_ci#endif
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* This ENC_MISC(3,encMisc3Arg) command is critical - without
35662306a36Sopenharmony_ci	   it there will eventually be video corruption.  Also, the
35762306a36Sopenharmony_ci	   saa7115 case is strange - the Windows driver is passing 1
35862306a36Sopenharmony_ci	   regardless of device type but if we have 1 for saa7115
35962306a36Sopenharmony_ci	   devices the video turns sluggish.  */
36062306a36Sopenharmony_ci	if (hdw->hdw_desc->flag_has_cx25840) {
36162306a36Sopenharmony_ci		encMisc3Arg = 1;
36262306a36Sopenharmony_ci	} else {
36362306a36Sopenharmony_ci		encMisc3Arg = 0;
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,
36662306a36Sopenharmony_ci				 encMisc3Arg,0,0);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci#if 0
37162306a36Sopenharmony_ci	/* This ENC_MISC(4,1) command is poisonous, so it is commented
37262306a36Sopenharmony_ci	   out.  But I'm leaving it here anyway to document its
37362306a36Sopenharmony_ci	   existence in the Windows driver.  The effect of this
37462306a36Sopenharmony_ci	   command is that apps displaying the stream become sluggish
37562306a36Sopenharmony_ci	   with stuttering video. */
37662306a36Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0);
37762306a36Sopenharmony_ci#endif
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0);
38062306a36Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/* prevent the PTSs from slowly drifting away in the generated
38362306a36Sopenharmony_ci	   MPEG stream */
38462306a36Sopenharmony_ci	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return ret;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ciint pvr2_encoder_adjust(struct pvr2_hdw *hdw)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	int ret;
39262306a36Sopenharmony_ci	ret = cx2341x_update(hdw,pvr2_encoder_cmd,
39362306a36Sopenharmony_ci			     (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
39462306a36Sopenharmony_ci			     &hdw->enc_ctl_state);
39562306a36Sopenharmony_ci	if (ret) {
39662306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
39762306a36Sopenharmony_ci			   "Error from cx2341x module code=%d",ret);
39862306a36Sopenharmony_ci	} else {
39962306a36Sopenharmony_ci		hdw->enc_cur_state = hdw->enc_ctl_state;
40062306a36Sopenharmony_ci		hdw->enc_cur_valid = !0;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci	return ret;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ciint pvr2_encoder_configure(struct pvr2_hdw *hdw)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	int ret;
40962306a36Sopenharmony_ci	int val;
41062306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_ENCODER, "pvr2_encoder_configure (cx2341x module)");
41162306a36Sopenharmony_ci	hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING;
41262306a36Sopenharmony_ci	hdw->enc_ctl_state.width = hdw->res_hor_val;
41362306a36Sopenharmony_ci	hdw->enc_ctl_state.height = hdw->res_ver_val;
41462306a36Sopenharmony_ci	hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ?
41562306a36Sopenharmony_ci				      0 : 1);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	ret = 0;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	ret |= pvr2_encoder_prep_config(hdw);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* saa7115: 0xf0 */
42262306a36Sopenharmony_ci	val = 0xf0;
42362306a36Sopenharmony_ci	if (hdw->hdw_desc->flag_has_cx25840) {
42462306a36Sopenharmony_ci		/* ivtv cx25840: 0x140 */
42562306a36Sopenharmony_ci		val = 0x140;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (!ret) ret = pvr2_encoder_vcmd(
42962306a36Sopenharmony_ci		hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
43062306a36Sopenharmony_ci		val, val);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* setup firmware to notify us about some events (don't know why...) */
43362306a36Sopenharmony_ci	if (!ret) ret = pvr2_encoder_vcmd(
43462306a36Sopenharmony_ci		hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
43562306a36Sopenharmony_ci		0, 0, 0x10000000, 0xffffffff);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (!ret) ret = pvr2_encoder_vcmd(
43862306a36Sopenharmony_ci		hdw,CX2341X_ENC_SET_VBI_LINE, 5,
43962306a36Sopenharmony_ci		0xffffffff,0,0,0,0);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (ret) {
44262306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
44362306a36Sopenharmony_ci			   "Failed to configure cx23416");
44462306a36Sopenharmony_ci		return ret;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	ret = pvr2_encoder_adjust(hdw);
44862306a36Sopenharmony_ci	if (ret) return ret;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	ret = pvr2_encoder_vcmd(
45162306a36Sopenharmony_ci		hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (ret) {
45462306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
45562306a36Sopenharmony_ci			   "Failed to initialize cx23416 video input");
45662306a36Sopenharmony_ci		return ret;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return 0;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ciint pvr2_encoder_start(struct pvr2_hdw *hdw)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	int status;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* unmask some interrupts */
46862306a36Sopenharmony_ci	pvr2_write_register(hdw, 0x0048, 0xbfffffff);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
47162306a36Sopenharmony_ci			  hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	switch (hdw->active_stream_type) {
47462306a36Sopenharmony_ci	case pvr2_config_vbi:
47562306a36Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
47662306a36Sopenharmony_ci					   0x01,0x14);
47762306a36Sopenharmony_ci		break;
47862306a36Sopenharmony_ci	case pvr2_config_mpeg:
47962306a36Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
48062306a36Sopenharmony_ci					   0,0x13);
48162306a36Sopenharmony_ci		break;
48262306a36Sopenharmony_ci	default: /* Unhandled cases for now */
48362306a36Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
48462306a36Sopenharmony_ci					   0,0x13);
48562306a36Sopenharmony_ci		break;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci	return status;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ciint pvr2_encoder_stop(struct pvr2_hdw *hdw)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	int status;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* mask all interrupts */
49562306a36Sopenharmony_ci	pvr2_write_register(hdw, 0x0048, 0xffffffff);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	switch (hdw->active_stream_type) {
49862306a36Sopenharmony_ci	case pvr2_config_vbi:
49962306a36Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
50062306a36Sopenharmony_ci					   0x01,0x01,0x14);
50162306a36Sopenharmony_ci		break;
50262306a36Sopenharmony_ci	case pvr2_config_mpeg:
50362306a36Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
50462306a36Sopenharmony_ci					   0x01,0,0x13);
50562306a36Sopenharmony_ci		break;
50662306a36Sopenharmony_ci	default: /* Unhandled cases for now */
50762306a36Sopenharmony_ci		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
50862306a36Sopenharmony_ci					   0x01,0,0x13);
50962306a36Sopenharmony_ci		break;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	return status;
51362306a36Sopenharmony_ci}
514