// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com >
*
* Original author:
* Ben Collins <bcollins@ubuntu.com>
*
* Additional work by:
* John Brooks <john.brooks@bluecherry.net>
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include "solo6x10.h"
#include "solo6x10-tw28.h"
#define DEFAULT_HDELAY_NTSC (
32 -
8 )
#define DEFAULT_HACTIVE_NTSC (
720 +
16 )
#define DEFAULT_VDELAY_NTSC (
7 -
2 )
#define DEFAULT_VACTIVE_NTSC (
240 +
4 )
#define DEFAULT_HDELAY_PAL (
32 +
4 )
#define DEFAULT_HACTIVE_PAL (
864 -DEFAULT_HDELAY_PAL)
#define DEFAULT_VDELAY_PAL (
6 )
#define DEFAULT_VACTIVE_PAL (
312 -DEFAULT_VDELAY_PAL)
static const u8 tbl_tw2864_ntsc_template[] = {
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x02,
/* 0x00 */
0 x12,
0 xf5,
0 x0c,
0 xd0,
0 x00,
0 x00,
0 x00,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x02,
/* 0x10 */
0 x12,
0 xf5,
0 x0c,
0 xd0,
0 x00,
0 x00,
0 x00,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x02,
/* 0x20 */
0 x12,
0 xf5,
0 x0c,
0 xd0,
0 x00,
0 x00,
0 x00,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x02,
/* 0x30 */
0 x12,
0 xf5,
0 x0c,
0 xd0,
0 x00,
0 x00,
0 x00,
0 x7f,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x40 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x50 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x60 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x70 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 xA3,
0 x00,
0 x00,
0 x02,
0 x00,
0 xcc,
0 x00,
0 x80,
0 x44,
0 x50,
/* 0x80 */
0 x22,
0 x01,
0 xd8,
0 xbc,
0 xb8,
0 x44,
0 x38,
0 x00,
0 x00,
0 x78,
0 x72,
0 x3e,
0 x14,
0 xa5,
0 xe4,
0 x05,
/* 0x90 */
0 x00,
0 x28,
0 x44,
0 x44,
0 xa0,
0 x88,
0 x5a,
0 x01,
0 x08,
0 x08,
0 x08,
0 x08,
0 x1a,
0 x1a,
0 x1a,
0 x1a,
/* 0xa0 */
0 x00,
0 x00,
0 x00,
0 xf0,
0 xf0,
0 xf0,
0 xf0,
0 x44,
0 x44,
0 x0a,
0 x00,
0 xff,
0 xef,
0 xef,
0 xef,
0 xef,
/* 0xb0 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0xc0 */
0 x00,
0 x00,
0 x55,
0 x00,
0 xb1,
0 xe4,
0 x40,
0 x00,
0 x77,
0 x77,
0 x01,
0 x13,
0 x57,
0 x9b,
0 xdf,
0 x20,
/* 0xd0 */
0 x64,
0 xa8,
0 xec,
0 xc1,
0 x0f,
0 x11,
0 x11,
0 x81,
0 x00,
0 xe0,
0 xbb,
0 xbb,
0 x00,
0 x11,
0 x00,
0 x00,
/* 0xe0 */
0 x11,
0 x00,
0 x00,
0 x11,
0 x00,
0 x00,
0 x11,
0 x00,
0 x83,
0 xb5,
0 x09,
0 x78,
0 x85,
0 x00,
0 x01,
0 x20,
/* 0xf0 */
0 x64,
0 x11,
0 x40,
0 xaf,
0 xff,
0 x00,
0 x00,
0 x00,
};
static const u8 tbl_tw2864_pal_template[] = {
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x12,
/* 0x00 */
0 x18,
0 xf5,
0 x0c,
0 xd0,
0 x00,
0 x00,
0 x01,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x12,
/* 0x10 */
0 x18,
0 xf5,
0 x0c,
0 xd0,
0 x00,
0 x00,
0 x01,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x12,
/* 0x20 */
0 x18,
0 xf5,
0 x0c,
0 xd0,
0 x00,
0 x00,
0 x01,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x12,
/* 0x30 */
0 x18,
0 xf5,
0 x0c,
0 xd0,
0 x00,
0 x00,
0 x01,
0 x7f,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x40 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x50 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x60 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x70 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 xA3,
0 x00,
0 x00,
0 x02,
0 x00,
0 xcc,
0 x00,
0 x80,
0 x44,
0 x50,
/* 0x80 */
0 x22,
0 x01,
0 xd8,
0 xbc,
0 xb8,
0 x44,
0 x38,
0 x00,
0 x00,
0 x78,
0 x72,
0 x3e,
0 x14,
0 xa5,
0 xe4,
0 x05,
/* 0x90 */
0 x00,
0 x28,
0 x44,
0 x44,
0 xa0,
0 x90,
0 x5a,
0 x01,
0 x0a,
0 x0a,
0 x0a,
0 x0a,
0 x1a,
0 x1a,
0 x1a,
0 x1a,
/* 0xa0 */
0 x00,
0 x00,
0 x00,
0 xf0,
0 xf0,
0 xf0,
0 xf0,
0 x44,
0 x44,
0 x0a,
0 x00,
0 xff,
0 xef,
0 xef,
0 xef,
0 xef,
/* 0xb0 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0xc0 */
0 x00,
0 x00,
0 x55,
0 x00,
0 xb1,
0 xe4,
0 x40,
0 x00,
0 x77,
0 x77,
0 x01,
0 x13,
0 x57,
0 x9b,
0 xdf,
0 x20,
/* 0xd0 */
0 x64,
0 xa8,
0 xec,
0 xc1,
0 x0f,
0 x11,
0 x11,
0 x81,
0 x00,
0 xe0,
0 xbb,
0 xbb,
0 x00,
0 x11,
0 x00,
0 x00,
/* 0xe0 */
0 x11,
0 x00,
0 x00,
0 x11,
0 x00,
0 x00,
0 x11,
0 x00,
0 x83,
0 xb5,
0 x09,
0 x00,
0 xa0,
0 x00,
0 x01,
0 x20,
/* 0xf0 */
0 x64,
0 x11,
0 x40,
0 xaf,
0 xff,
0 x00,
0 x00,
0 x00,
};
static const u8 tbl_tw2865_ntsc_template[] = {
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x02,
/* 0x00 */
0 x12,
0 xff,
0 x09,
0 xd0,
0 x00,
0 x00,
0 x00,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x02,
/* 0x10 */
0 x12,
0 xff,
0 x09,
0 xd0,
0 x00,
0 x00,
0 x00,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x02,
/* 0x20 */
0 x12,
0 xff,
0 x09,
0 xd0,
0 x00,
0 x00,
0 x00,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x48,
0 x80,
0 x80,
0 x00,
0 x02,
/* 0x30 */
0 x12,
0 xff,
0 x09,
0 xd0,
0 x00,
0 x00,
0 x00,
0 x7f,
0 x00,
0 x00,
0 x90,
0 x68,
0 x00,
0 x38,
0 x80,
0 x80,
/* 0x40 */
0 x80,
0 x80,
0 x77,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x50 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x45,
0 x11,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x60 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x21,
0 x43,
0 x08,
0 x00,
0 x00,
0 x01,
0 xf1,
0 x03,
0 xEF,
0 x03,
/* 0x70 */
0 xE9,
0 x03,
0 xD9,
0 x15,
0 x15,
0 xE4,
0 xA3,
0 x80,
0 x00,
0 x02,
0 x00,
0 xCC,
0 x00,
0 x80,
0 x44,
0 x50,
/* 0x80 */
0 x22,
0 x01,
0 xD8,
0 xBC,
0 xB8,
0 x44,
0 x38,
0 x00,
0 x00,
0 x78,
0 x44,
0 x3D,
0 x14,
0 xA5,
0 xE0,
0 x05,
/* 0x90 */
0 x00,
0 x28,
0 x44,
0 x44,
0 xA0,
0 x90,
0 x52,
0 x13,
0 x08,
0 x08,
0 x08,
0 x08,
0 x1A,
0 x1A,
0 x1B,
0 x1A,
/* 0xa0 */
0 x00,
0 x00,
0 x00,
0 xF0,
0 xF0,
0 xF0,
0 xF0,
0 x44,
0 x44,
0 x4A,
0 x00,
0 xFF,
0 xEF,
0 xEF,
0 xEF,
0 xEF,
/* 0xb0 */
0 xFF,
0 xE7,
0 xE9,
0 xE9,
0 xEB,
0 xFF,
0 xD6,
0 xD8,
0 xD8,
0 xD7,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0xc0 */
0 x00,
0 x00,
0 x55,
0 x00,
0 xE4,
0 x39,
0 x00,
0 x80,
0 x77,
0 x77,
0 x03,
0 x20,
0 x57,
0 x9b,
0 xdf,
0 x31,
/* 0xd0 */
0 x64,
0 xa8,
0 xec,
0 xd1,
0 x0f,
0 x11,
0 x11,
0 x81,
0 x10,
0 xC0,
0 xAA,
0 xAA,
0 x00,
0 x11,
0 x00,
0 x00,
/* 0xe0 */
0 x11,
0 x00,
0 x00,
0 x11,
0 x00,
0 x00,
0 x11,
0 x00,
0 x83,
0 xB5,
0 x09,
0 x78,
0 x85,
0 x00,
0 x01,
0 x20,
/* 0xf0 */
0 x64,
0 x51,
0 x40,
0 xaf,
0 xFF,
0 xF0,
0 x00,
0 xC0,
};
static const u8 tbl_tw2865_pal_template[] = {
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x12,
/* 0x00 */
0 x11,
0 xff,
0 x01,
0 xc3,
0 x00,
0 x00,
0 x01,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x12,
/* 0x10 */
0 x11,
0 xff,
0 x01,
0 xc3,
0 x00,
0 x00,
0 x01,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x12,
/* 0x20 */
0 x11,
0 xff,
0 x01,
0 xc3,
0 x00,
0 x00,
0 x01,
0 x7f,
0 x00,
0 xf0,
0 x70,
0 x30,
0 x80,
0 x80,
0 x00,
0 x12,
/* 0x30 */
0 x11,
0 xff,
0 x01,
0 xc3,
0 x00,
0 x00,
0 x01,
0 x7f,
0 x00,
0 x94,
0 x90,
0 x48,
0 x00,
0 x38,
0 x7F,
0 x80,
/* 0x40 */
0 x80,
0 x80,
0 x77,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x50 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x45,
0 x11,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0x60 */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x21,
0 x43,
0 x08,
0 x00,
0 x00,
0 x01,
0 xf1,
0 x03,
0 xEF,
0 x03,
/* 0x70 */
0 xEA,
0 x03,
0 xD9,
0 x15,
0 x15,
0 xE4,
0 xA3,
0 x80,
0 x00,
0 x02,
0 x00,
0 xCC,
0 x00,
0 x80,
0 x44,
0 x50,
/* 0x80 */
0 x22,
0 x01,
0 xD8,
0 xBC,
0 xB8,
0 x44,
0 x38,
0 x00,
0 x00,
0 x78,
0 x44,
0 x3D,
0 x14,
0 xA5,
0 xE0,
0 x05,
/* 0x90 */
0 x00,
0 x28,
0 x44,
0 x44,
0 xA0,
0 x90,
0 x52,
0 x13,
0 x08,
0 x08,
0 x08,
0 x08,
0 x1A,
0 x1A,
0 x1A,
0 x1A,
/* 0xa0 */
0 x00,
0 x00,
0 x00,
0 xF0,
0 xF0,
0 xF0,
0 xF0,
0 x44,
0 x44,
0 x4A,
0 x00,
0 xFF,
0 xEF,
0 xEF,
0 xEF,
0 xEF,
/* 0xb0 */
0 xFF,
0 xE7,
0 xE9,
0 xE9,
0 xE9,
0 xFF,
0 xD7,
0 xD8,
0 xD9,
0 xD8,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* 0xc0 */
0 x00,
0 x00,
0 x55,
0 x00,
0 xE4,
0 x39,
0 x00,
0 x80,
0 x77,
0 x77,
0 x03,
0 x20,
0 x57,
0 x9b,
0 xdf,
0 x31,
/* 0xd0 */
0 x64,
0 xa8,
0 xec,
0 xd1,
0 x0f,
0 x11,
0 x11,
0 x81,
0 x10,
0 xC0,
0 xAA,
0 xAA,
0 x00,
0 x11,
0 x00,
0 x00,
/* 0xe0 */
0 x11,
0 x00,
0 x00,
0 x11,
0 x00,
0 x00,
0 x11,
0 x00,
0 x83,
0 xB5,
0 x09,
0 x00,
0 xA0,
0 x00,
0 x01,
0 x20,
/* 0xf0 */
0 x64,
0 x51,
0 x40,
0 xaf,
0 xFF,
0 xF0,
0 x00,
0 xC0,
};
#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (
1 << __id)))
static u8 tw_readbyte(
struct solo_dev *solo_dev,
int chip_id, u8 tw6x_off,
u8 tw_off)
{
if (is_tw286x(solo_dev, chip_id))
return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_id),
tw6x_off);
else
return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_id),
tw_off);
}
static void tw_writebyte(
struct solo_dev *solo_dev,
int chip_id,
u8 tw6x_off, u8 tw_off, u8 val)
{
if (is_tw286x(solo_dev, chip_id))
solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_id),
tw6x_off, val);
else
solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_id),
tw_off, val);
}
static void tw_write_and_verify(
struct solo_dev *solo_dev, u8 addr, u8 off,
u8 val)
{
int i;
for (i =
0 ; i <
5 ; i++) {
u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
if (rval == val)
return ;
solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
msleep_interruptible(
1 );
}
/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
/* addr, off, val); */
}
static int tw2865_setup(
struct solo_dev *solo_dev, u8 dev_addr)
{
u8 tbl_tw2865_common[
256 ];
int i;
if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
memcpy(tbl_tw2865_common, tbl_tw2865_pal_template,
sizeof (tbl_tw2865_common));
else
memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
sizeof (tbl_tw2865_common));
/* ALINK Mode */
if (solo_dev->nr_chans ==
4 ) {
tbl_tw2865_common[
0 xd2] =
0 x01;
tbl_tw2865_common[
0 xcf] =
0 x00;
}
else if (solo_dev->nr_chans ==
8 ) {
tbl_tw2865_common[
0 xd2] =
0 x02;
if (dev_addr == TW_CHIP_OFFSET_ADDR(
1 ))
tbl_tw2865_common[
0 xcf] =
0 x80;
}
else if (solo_dev->nr_chans ==
16 ) {
tbl_tw2865_common[
0 xd2] =
0 x03;
if (dev_addr == TW_CHIP_OFFSET_ADDR(
1 ))
tbl_tw2865_common[
0 xcf] =
0 x83;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
2 ))
tbl_tw2865_common[
0 xcf] =
0 x83;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
3 ))
tbl_tw2865_common[
0 xcf] =
0 x80;
}
for (i =
0 ; i <
0 xff; i++) {
/* Skip read only registers */
switch (i) {
case 0 xb8 ...
0 xc1:
case 0 xc4 ...
0 xc7:
case 0 xfd:
continue ;
}
switch (i & ~
0 x30) {
case 0 x00:
case 0 x0c ...
0 x0d:
continue ;
}
tw_write_and_verify(solo_dev, dev_addr, i,
tbl_tw2865_common[i]);
}
return 0 ;
}
static int tw2864_setup(
struct solo_dev *solo_dev, u8 dev_addr)
{
u8 tbl_tw2864_common[
256 ];
int i;
if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
memcpy(tbl_tw2864_common, tbl_tw2864_pal_template,
sizeof (tbl_tw2864_common));
else
memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template,
sizeof (tbl_tw2864_common));
if (solo_dev->tw2865 ==
0 ) {
/* IRQ Mode */
if (solo_dev->nr_chans ==
4 ) {
tbl_tw2864_common[
0 xd2] =
0 x01;
tbl_tw2864_common[
0 xcf] =
0 x00;
}
else if (solo_dev->nr_chans ==
8 ) {
tbl_tw2864_common[
0 xd2] =
0 x02;
if (dev_addr == TW_CHIP_OFFSET_ADDR(
0 ))
tbl_tw2864_common[
0 xcf] =
0 x43;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
1 ))
tbl_tw2864_common[
0 xcf] =
0 x40;
}
else if (solo_dev->nr_chans ==
16 ) {
tbl_tw2864_common[
0 xd2] =
0 x03;
if (dev_addr == TW_CHIP_OFFSET_ADDR(
0 ))
tbl_tw2864_common[
0 xcf] =
0 x43;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
1 ))
tbl_tw2864_common[
0 xcf] =
0 x43;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
2 ))
tbl_tw2864_common[
0 xcf] =
0 x43;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
3 ))
tbl_tw2864_common[
0 xcf] =
0 x40;
}
}
else {
/* ALINK Mode. Assumes that the first tw28xx is a
* 2865 and these are in cascade. */
for (i =
0 ; i <=
4 ; i++)
tbl_tw2864_common[
0 x08 | i <<
4 ] =
0 x12;
if (solo_dev->nr_chans ==
8 ) {
tbl_tw2864_common[
0 xd2] =
0 x02;
if (dev_addr == TW_CHIP_OFFSET_ADDR(
1 ))
tbl_tw2864_common[
0 xcf] =
0 x80;
}
else if (solo_dev->nr_chans ==
16 ) {
tbl_tw2864_common[
0 xd2] =
0 x03;
if (dev_addr == TW_CHIP_OFFSET_ADDR(
1 ))
tbl_tw2864_common[
0 xcf] =
0 x83;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
2 ))
tbl_tw2864_common[
0 xcf] =
0 x83;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
3 ))
tbl_tw2864_common[
0 xcf] =
0 x80;
}
}
for (i =
0 ; i <
0 xff; i++) {
/* Skip read only registers */
switch (i) {
case 0 xb8 ...
0 xc1:
case 0 xfd:
continue ;
}
switch (i & ~
0 x30) {
case 0 x00:
case 0 x0c:
case 0 x0d:
continue ;
}
tw_write_and_verify(solo_dev, dev_addr, i,
tbl_tw2864_common[i]);
}
return 0 ;
}
static int tw2815_setup(
struct solo_dev *solo_dev, u8 dev_addr)
{
u8 tbl_ntsc_tw2815_common[] = {
0 x00,
0 xc8,
0 x20,
0 xd0,
0 x06,
0 xf0,
0 x08,
0 x80,
0 x80,
0 x80,
0 x80,
0 x02,
0 x06,
0 x00,
0 x11,
};
u8 tbl_pal_tw2815_common[] = {
0 x00,
0 x88,
0 x20,
0 xd0,
0 x05,
0 x20,
0 x28,
0 x80,
0 x80,
0 x80,
0 x80,
0 x82,
0 x06,
0 x00,
0 x11,
};
u8 tbl_tw2815_sfr[] = {
0 x00,
0 x00,
0 x00,
0 xc0,
0 x45,
0 xa0,
0 xd0,
0 x2f,
/* 0x00 */
0 x64,
0 x80,
0 x80,
0 x82,
0 x82,
0 x00,
0 x00,
0 x00,
0 x00,
0 x0f,
0 x05,
0 x00,
0 x00,
0 x80,
0 x06,
0 x00,
/* 0x10 */
0 x00,
0 x00,
0 x00,
0 xff,
0 x8f,
0 x00,
0 x00,
0 x00,
0 x88,
0 x88,
0 xc0,
0 x00,
0 x20,
0 x64,
0 xa8,
0 xec,
/* 0x20 */
0 x31,
0 x75,
0 xb9,
0 xfd,
0 x00,
0 x00,
0 x88,
0 x88,
0 x88,
0 x11,
0 x00,
0 x88,
0 x88,
0 x00,
/* 0x30 */
};
u8 *tbl_tw2815_common;
int i;
int ch;
tbl_ntsc_tw2815_common[
0 x06] =
0 ;
/* Horizontal Delay Control */
tbl_ntsc_tw2815_common[
0 x02] = DEFAULT_HDELAY_NTSC &
0 xff;
tbl_ntsc_tw2815_common[
0 x06] |=
0 x03 & (DEFAULT_HDELAY_NTSC >>
8 );
/* Horizontal Active Control */
tbl_ntsc_tw2815_common[
0 x03] = DEFAULT_HACTIVE_NTSC &
0 xff;
tbl_ntsc_tw2815_common[
0 x06] |=
((
0 x03 & (DEFAULT_HACTIVE_NTSC >>
8 )) <<
2 );
/* Vertical Delay Control */
tbl_ntsc_tw2815_common[
0 x04] = DEFAULT_VDELAY_NTSC &
0 xff;
tbl_ntsc_tw2815_common[
0 x06] |=
((
0 x01 & (DEFAULT_VDELAY_NTSC >>
8 )) <<
4 );
/* Vertical Active Control */
tbl_ntsc_tw2815_common[
0 x05] = DEFAULT_VACTIVE_NTSC &
0 xff;
tbl_ntsc_tw2815_common[
0 x06] |=
((
0 x01 & (DEFAULT_VACTIVE_NTSC >>
8 )) <<
5 );
tbl_pal_tw2815_common[
0 x06] =
0 ;
/* Horizontal Delay Control */
tbl_pal_tw2815_common[
0 x02] = DEFAULT_HDELAY_PAL &
0 xff;
tbl_pal_tw2815_common[
0 x06] |=
0 x03 & (DEFAULT_HDELAY_PAL >>
8 );
/* Horizontal Active Control */
tbl_pal_tw2815_common[
0 x03] = DEFAULT_HACTIVE_PAL &
0 xff;
tbl_pal_tw2815_common[
0 x06] |=
((
0 x03 & (DEFAULT_HACTIVE_PAL >>
8 )) <<
2 );
/* Vertical Delay Control */
tbl_pal_tw2815_common[
0 x04] = DEFAULT_VDELAY_PAL &
0 xff;
tbl_pal_tw2815_common[
0 x06] |=
((
0 x01 & (DEFAULT_VDELAY_PAL >>
8 )) <<
4 );
/* Vertical Active Control */
tbl_pal_tw2815_common[
0 x05] = DEFAULT_VACTIVE_PAL &
0 xff;
tbl_pal_tw2815_common[
0 x06] |=
((
0 x01 & (DEFAULT_VACTIVE_PAL >>
8 )) <<
5 );
tbl_tw2815_common =
(solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
/* Dual ITU-R BT.656 format */
tbl_tw2815_common[
0 x0d] |=
0 x04;
/* Audio configuration */
tbl_tw2815_sfr[
0 x62 -
0 x40] &= ~(
3 <<
6 );
if (solo_dev->nr_chans ==
4 ) {
tbl_tw2815_sfr[
0 x63 -
0 x40] |=
1 ;
tbl_tw2815_sfr[
0 x62 -
0 x40] |=
3 <<
6 ;
}
else if (solo_dev->nr_chans ==
8 ) {
tbl_tw2815_sfr[
0 x63 -
0 x40] |=
2 ;
if (dev_addr == TW_CHIP_OFFSET_ADDR(
0 ))
tbl_tw2815_sfr[
0 x62 -
0 x40] |=
1 <<
6 ;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
1 ))
tbl_tw2815_sfr[
0 x62 -
0 x40] |=
2 <<
6 ;
}
else if (solo_dev->nr_chans ==
16 ) {
tbl_tw2815_sfr[
0 x63 -
0 x40] |=
3 ;
if (dev_addr == TW_CHIP_OFFSET_ADDR(
0 ))
tbl_tw2815_sfr[
0 x62 -
0 x40] |=
1 <<
6 ;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
1 ))
tbl_tw2815_sfr[
0 x62 -
0 x40] |=
0 <<
6 ;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
2 ))
tbl_tw2815_sfr[
0 x62 -
0 x40] |=
0 <<
6 ;
else if (dev_addr == TW_CHIP_OFFSET_ADDR(
3 ))
tbl_tw2815_sfr[
0 x62 -
0 x40] |=
2 <<
6 ;
}
/* Output mode of R_ADATM pin (0 mixing, 1 record) */
/* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
/* 8KHz, used to be 16KHz, but changed for remote client compat */
tbl_tw2815_sfr[
0 x62 -
0 x40] |=
0 <<
2 ;
tbl_tw2815_sfr[
0 x6c -
0 x40] |=
0 <<
2 ;
/* Playback of right channel */
tbl_tw2815_sfr[
0 x6c -
0 x40] |=
1 <<
5 ;
/* Reserved value (XXX ??) */
tbl_tw2815_sfr[
0 x5c -
0 x40] |=
1 <<
5 ;
/* Analog output gain and mix ratio playback on full */
tbl_tw2815_sfr[
0 x70 -
0 x40] |=
0 xff;
/* Select playback audio and mute all except */
tbl_tw2815_sfr[
0 x71 -
0 x40] |=
0 x10;
tbl_tw2815_sfr[
0 x6d -
0 x40] |=
0 x0f;
/* End of audio configuration */
for (ch =
0 ; ch <
4 ; ch++) {
tbl_tw2815_common[
0 x0d] &= ~
3 ;
switch (ch) {
case 0 :
tbl_tw2815_common[
0 x0d] |=
0 x21;
break ;
case 1 :
tbl_tw2815_common[
0 x0d] |=
0 x20;
break ;
case 2 :
tbl_tw2815_common[
0 x0d] |=
0 x23;
break ;
case 3 :
tbl_tw2815_common[
0 x0d] |=
0 x22;
break ;
}
for (i =
0 ; i <
0 x0f; i++) {
if (i ==
0 x00)
continue ;
/* read-only */
solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
dev_addr, (ch *
0 x10) + i,
tbl_tw2815_common[i]);
}
}
for (i =
0 x40; i <
0 x76; i++) {
/* Skip read-only and nop registers */
if (i ==
0 x40 || i ==
0 x59 || i ==
0 x5a ||
i ==
0 x5d || i ==
0 x5e || i ==
0 x5f)
continue ;
solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
tbl_tw2815_sfr[i -
0 x40]);
}
return 0 ;
}
#define FIRST_ACTIVE_LINE
0 x0008
#define LAST_ACTIVE_LINE
0 x0102
static void saa712x_write_regs(
struct solo_dev *dev,
const u8 *vals,
int start,
int n)
{
for (; start < n; start++, vals++) {
/* Skip read-only registers */
switch (start) {
/* case 0x00 ... 0x25: */
case 0 x2e ...
0 x37:
case 0 x60:
case 0 x7d:
continue ;
}
solo_i2c_writebyte(dev, SOLO_I2C_SAA,
0 x46, start, *vals);
}
}
#define SAA712x_reg7c (
0 x80 | ((LAST_ACTIVE_LINE &
0 x100) >>
2 ) \
| ((FIRST_ACTIVE_LINE &
0 x100) >>
4 ))
static void saa712x_setup(
struct solo_dev *dev)
{
const int reg_start =
0 x26;
static const u8 saa7128_regs_ntsc[] = {
/* :0x26 */
0 x0d,
0 x00,
/* :0x28 */
0 x59,
0 x1d,
0 x75,
0 x3f,
0 x06,
0 x3f,
/* :0x2e XXX: read-only */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* :0x38 */
0 x1a,
0 x1a,
0 x13,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* :0x40 */
0 x00,
0 x00,
0 x00,
0 x68,
0 x10,
0 x97,
0 x4c,
0 x18,
0 x9b,
0 x93,
0 x9f,
0 xff,
0 x7c,
0 x34,
0 x3f,
0 x3f,
/* :0x50 */
0 x3f,
0 x83,
0 x83,
0 x80,
0 x0d,
0 x0f,
0 xc3,
0 x06,
0 x02,
0 x80,
0 x71,
0 x77,
0 xa7,
0 x67,
0 x66,
0 x2e,
/* :0x60 */
0 x7b,
0 x11,
0 x4f,
0 x1f,
0 x7c,
0 xf0,
0 x21,
0 x77,
0 x41,
0 x88,
0 x41,
0 x52,
0 xed,
0 x10,
0 x10,
0 x00,
/* :0x70 */
0 x41,
0 xc3,
0 x00,
0 x3e,
0 xb8,
0 x02,
0 x00,
0 x00,
0 x00,
0 x00, FIRST_ACTIVE_LINE, LAST_ACTIVE_LINE &
0 xff,
SAA712x_reg7c,
0 x00,
0 xff,
0 xff,
}, saa7128_regs_pal[] = {
/* :0x26 */
0 x0d,
0 x00,
/* :0x28 */
0 xe1,
0 x1d,
0 x75,
0 x3f,
0 x06,
0 x3f,
/* :0x2e XXX: read-only */
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* :0x38 */
0 x1a,
0 x1a,
0 x13,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
/* :0x40 */
0 x00,
0 x00,
0 x00,
0 x68,
0 x10,
0 x97,
0 x4c,
0 x18,
0 x9b,
0 x93,
0 x9f,
0 xff,
0 x7c,
0 x34,
0 x3f,
0 x3f,
/* :0x50 */
0 x3f,
0 x83,
0 x83,
0 x80,
0 x0d,
0 x0f,
0 xc3,
0 x06,
0 x02,
0 x80,
0 x0f,
0 x77,
0 xa7,
0 x67,
0 x66,
0 x2e,
/* :0x60 */
0 x7b,
0 x02,
0 x35,
0 xcb,
0 x8a,
0 x09,
0 x2a,
0 x77,
0 x41,
0 x88,
0 x41,
0 x52,
0 xf1,
0 x10,
0 x20,
0 x00,
/* :0x70 */
0 x41,
0 xc3,
0 x00,
0 x3e,
0 xb8,
0 x02,
0 x00,
0 x00,
0 x00,
0 x00,
0 x12,
0 x30,
SAA712x_reg7c |
0 x40,
0 x00,
0 xff,
0 xff,
};
if (dev->video_type == SOLO_VO_FMT_TYPE_PAL)
saa712x_write_regs(dev, saa7128_regs_pal, reg_start,
sizeof (saa7128_regs_pal));
else
saa712x_write_regs(dev, saa7128_regs_ntsc, reg_start,
sizeof (saa7128_regs_ntsc));
}
int solo_tw28_init(
struct solo_dev *solo_dev)
{
int i;
u8 value;
solo_dev->tw28_cnt =
0 ;
/* Detect techwell chip type(s) */
for (i =
0 ; i < solo_dev->nr_chans /
4 ; i++) {
value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(i),
0 xFF);
switch (value >>
3 ) {
case 0 x18:
solo_dev->tw2865 |=
1 << i;
solo_dev->tw28_cnt++;
break ;
case 0 x0c:
case 0 x0d:
solo_dev->tw2864 |=
1 << i;
solo_dev->tw28_cnt++;
break ;
default :
value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(i),
0 x59);
if ((value >>
3 ) ==
0 x04) {
solo_dev->tw2815 |=
1 << i;
solo_dev->tw28_cnt++;
}
}
}
if (solo_dev->tw28_cnt != (solo_dev->nr_chans >>
2 )) {
dev_err(&solo_dev->pdev->dev,
"Could not initialize any techwell chips\n" );
return -EINVAL;
}
saa712x_setup(solo_dev);
for (i =
0 ; i < solo_dev->tw28_cnt; i++) {
if ((solo_dev->tw2865 & (
1 << i)))
tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
else if ((solo_dev->tw2864 & (
1 << i)))
tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
else
tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
}
return 0 ;
}
/*
* We accessed the video status signal in the Techwell chip through
* iic/i2c because the video status reported by register REG_VI_STATUS1
* (address 0x012C) of the SOLO6010 chip doesn't give the correct video
* status signal values.
*/
int tw28_get_video_status(
struct solo_dev *solo_dev, u8 ch)
{
u8 val, chip_num;
/* Get the right chip and on-chip channel */
chip_num = ch /
4 ;
ch %=
4 ;
val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR,
TW_AV_STAT_ADDR) &
0 x0f;
return val & (
1 << ch) ?
1 :
0 ;
}
#if 0
/* Status of audio from up to 4 techwell chips are combined into 1 variable.
* See techwell datasheet for details. */
u16 tw28_get_audio_status(
struct solo_dev *solo_dev)
{
u8 val;
u16 status =
0 ;
int i;
for (i =
0 ; i < solo_dev->tw28_cnt; i++) {
val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR,
TW_AV_STAT_ADDR) &
0 xf0) >>
4 ;
status |= val << (i *
4 );
}
return status;
}
#endif
bool tw28_has_sharpness(
struct solo_dev *solo_dev, u8 ch)
{
return is_tw286x(solo_dev, ch /
4 );
}
int tw28_set_ctrl_val(
struct solo_dev *solo_dev, u32 ctrl, u8 ch,
s32 val)
{
char sval;
u8 chip_num;
/* Get the right chip and on-chip channel */
chip_num = ch /
4 ;
ch %=
4 ;
if (val >
255 || val <
0 )
return -ERANGE;
switch (ctrl) {
case V4L2_CID_SHARPNESS:
/* Only 286x has sharpness */
if (is_tw286x(solo_dev, chip_num)) {
u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_num),
TW286x_SHARPNESS(chip_num));
v &=
0 xf0;
v |= val;
solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_num),
TW286x_SHARPNESS(chip_num), v);
}
else {
return -EINVAL;
}
break ;
case V4L2_CID_HUE:
if (is_tw286x(solo_dev, chip_num))
sval = val -
128 ;
else
sval = (
char )val;
tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
TW_HUE_ADDR(ch), sval);
break ;
case V4L2_CID_SATURATION:
/* 286x chips have a U and V component for saturation */
if (is_tw286x(solo_dev, chip_num)) {
solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_num),
TW286x_SATURATIONU_ADDR(ch), val);
}
tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
TW_SATURATION_ADDR(ch), val);
break ;
case V4L2_CID_CONTRAST:
tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
TW_CONTRAST_ADDR(ch), val);
break ;
case V4L2_CID_BRIGHTNESS:
if (is_tw286x(solo_dev, chip_num))
sval = val -
128 ;
else
sval = (
char )val;
tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
TW_BRIGHTNESS_ADDR(ch), sval);
break ;
default :
return -EINVAL;
}
return 0 ;
}
int tw28_get_ctrl_val(
struct solo_dev *solo_dev, u32 ctrl, u8 ch,
s32 *val)
{
u8 rval, chip_num;
/* Get the right chip and on-chip channel */
chip_num = ch /
4 ;
ch %=
4 ;
switch (ctrl) {
case V4L2_CID_SHARPNESS:
/* Only 286x has sharpness */
if (is_tw286x(solo_dev, chip_num)) {
rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
TW_CHIP_OFFSET_ADDR(chip_num),
TW286x_SHARPNESS(chip_num));
*val = rval &
0 x0f;
}
else
*val =
0 ;
break ;
case V4L2_CID_HUE:
rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
TW_HUE_ADDR(ch));
if (is_tw286x(solo_dev, chip_num))
*val = (s32)((
char )rval) +
128 ;
else
*val = rval;
break ;
case V4L2_CID_SATURATION:
*val = tw_readbyte(solo_dev, chip_num,
TW286x_SATURATIONU_ADDR(ch),
TW_SATURATION_ADDR(ch));
break ;
case V4L2_CID_CONTRAST:
*val = tw_readbyte(solo_dev, chip_num,
TW286x_CONTRAST_ADDR(ch),
TW_CONTRAST_ADDR(ch));
break ;
case V4L2_CID_BRIGHTNESS:
rval = tw_readbyte(solo_dev, chip_num,
TW286x_BRIGHTNESS_ADDR(ch),
TW_BRIGHTNESS_ADDR(ch));
if (is_tw286x(solo_dev, chip_num))
*val = (s32)((
char )rval) +
128 ;
else
*val = rval;
break ;
default :
return -EINVAL;
}
return 0 ;
}
#if 0
/*
* For audio output volume, the output channel is only 1. In this case we
* don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
* is the base address of the techwell chip.
*/
void tw2815_Set_AudioOutVol(
struct solo_dev *solo_dev,
unsigned int u_val)
{
unsigned int val;
unsigned int chip_num;
chip_num = (solo_dev->nr_chans -
1 ) /
4 ;
val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
TW_AUDIO_OUTPUT_VOL_ADDR);
u_val = (val &
0 x0f) | (u_val <<
4 );
tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
}
#endif
u8 tw28_get_audio_gain(
struct solo_dev *solo_dev, u8 ch)
{
u8 val;
u8 chip_num;
/* Get the right chip and on-chip channel */
chip_num = ch /
4 ;
ch %=
4 ;
val = tw_readbyte(solo_dev, chip_num,
TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
TW_AUDIO_INPUT_GAIN_ADDR(ch));
return (ch %
2 ) ? (val >>
4 ) : (val &
0 x0f);
}
void tw28_set_audio_gain(
struct solo_dev *solo_dev, u8 ch, u8 val)
{
u8 old_val;
u8 chip_num;
/* Get the right chip and on-chip channel */
chip_num = ch /
4 ;
ch %=
4 ;
old_val = tw_readbyte(solo_dev, chip_num,
TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
TW_AUDIO_INPUT_GAIN_ADDR(ch));
val = (old_val & ((ch %
2 ) ?
0 x0f :
0 xf0)) |
((ch %
2 ) ? (val <<
4 ) : val);
tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
TW_AUDIO_INPUT_GAIN_ADDR(ch), val);
}
Messung V0.5 in Prozent C=94 H=94 G=93
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland