Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com>
0004  *
0005  * Original author:
0006  * Ben Collins <bcollins@ubuntu.com>
0007  *
0008  * Additional work by:
0009  * John Brooks <john.brooks@bluecherry.net>
0010  */
0011 
0012 #include <linux/kernel.h>
0013 #include <linux/delay.h>
0014 
0015 #include "solo6x10.h"
0016 #include "solo6x10-tw28.h"
0017 
0018 #define DEFAULT_HDELAY_NTSC     (32 - 8)
0019 #define DEFAULT_HACTIVE_NTSC        (720 + 16)
0020 #define DEFAULT_VDELAY_NTSC     (7 - 2)
0021 #define DEFAULT_VACTIVE_NTSC        (240 + 4)
0022 
0023 #define DEFAULT_HDELAY_PAL      (32 + 4)
0024 #define DEFAULT_HACTIVE_PAL     (864-DEFAULT_HDELAY_PAL)
0025 #define DEFAULT_VDELAY_PAL      (6)
0026 #define DEFAULT_VACTIVE_PAL     (312-DEFAULT_VDELAY_PAL)
0027 
0028 
0029 static const u8 tbl_tw2864_ntsc_template[] = {
0030     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
0031     0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0032     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
0033     0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0034     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
0035     0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0036     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
0037     0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0038     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
0039     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0040     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
0041     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0042     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
0043     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0044     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
0045     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
0046     0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
0047     0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
0048     0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
0049     0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01,
0050     0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
0051     0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
0052     0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
0053     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0054     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
0055     0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
0056     0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
0057     0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
0058     0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
0059     0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
0060     0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
0061     0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
0062 };
0063 
0064 static const u8 tbl_tw2864_pal_template[] = {
0065     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
0066     0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
0067     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
0068     0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
0069     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
0070     0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
0071     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
0072     0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
0073     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
0074     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0075     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
0076     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0077     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
0078     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0079     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
0080     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
0081     0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
0082     0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
0083     0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
0084     0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01,
0085     0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
0086     0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
0087     0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
0088     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0089     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
0090     0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
0091     0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
0092     0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
0093     0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
0094     0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
0095     0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */
0096     0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
0097 };
0098 
0099 static const u8 tbl_tw2865_ntsc_template[] = {
0100     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
0101     0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0102     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
0103     0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0104     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
0105     0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0106     0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
0107     0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
0108     0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */
0109     0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
0110     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
0111     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0112     0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
0113     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
0114     0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
0115     0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
0116     0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
0117     0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
0118     0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
0119     0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
0120     0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */
0121     0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
0122     0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
0123     0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8,
0124     0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
0125     0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
0126     0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
0127     0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
0128     0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
0129     0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
0130     0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
0131     0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
0132 };
0133 
0134 static const u8 tbl_tw2865_pal_template[] = {
0135     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
0136     0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
0137     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
0138     0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
0139     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
0140     0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
0141     0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
0142     0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
0143     0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */
0144     0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
0145     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
0146     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0147     0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
0148     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
0149     0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
0150     0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
0151     0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
0152     0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
0153     0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
0154     0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
0155     0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */
0156     0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
0157     0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
0158     0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8,
0159     0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
0160     0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
0161     0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
0162     0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
0163     0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
0164     0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
0165     0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */
0166     0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
0167 };
0168 
0169 #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
0170 
0171 static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off,
0172               u8 tw_off)
0173 {
0174     if (is_tw286x(solo_dev, chip_id))
0175         return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
0176                      TW_CHIP_OFFSET_ADDR(chip_id),
0177                      tw6x_off);
0178     else
0179         return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
0180                      TW_CHIP_OFFSET_ADDR(chip_id),
0181                      tw_off);
0182 }
0183 
0184 static void tw_writebyte(struct solo_dev *solo_dev, int chip_id,
0185              u8 tw6x_off, u8 tw_off, u8 val)
0186 {
0187     if (is_tw286x(solo_dev, chip_id))
0188         solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
0189                    TW_CHIP_OFFSET_ADDR(chip_id),
0190                    tw6x_off, val);
0191     else
0192         solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
0193                    TW_CHIP_OFFSET_ADDR(chip_id),
0194                    tw_off, val);
0195 }
0196 
0197 static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
0198                 u8 val)
0199 {
0200     int i;
0201 
0202     for (i = 0; i < 5; i++) {
0203         u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
0204 
0205         if (rval == val)
0206             return;
0207 
0208         solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
0209         msleep_interruptible(1);
0210     }
0211 
0212 /*  printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
0213 /*      addr, off, val); */
0214 }
0215 
0216 static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
0217 {
0218     u8 tbl_tw2865_common[256];
0219     int i;
0220 
0221     if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
0222         memcpy(tbl_tw2865_common, tbl_tw2865_pal_template,
0223                sizeof(tbl_tw2865_common));
0224     else
0225         memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
0226                sizeof(tbl_tw2865_common));
0227 
0228     /* ALINK Mode */
0229     if (solo_dev->nr_chans == 4) {
0230         tbl_tw2865_common[0xd2] = 0x01;
0231         tbl_tw2865_common[0xcf] = 0x00;
0232     } else if (solo_dev->nr_chans == 8) {
0233         tbl_tw2865_common[0xd2] = 0x02;
0234         if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
0235             tbl_tw2865_common[0xcf] = 0x80;
0236     } else if (solo_dev->nr_chans == 16) {
0237         tbl_tw2865_common[0xd2] = 0x03;
0238         if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
0239             tbl_tw2865_common[0xcf] = 0x83;
0240         else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
0241             tbl_tw2865_common[0xcf] = 0x83;
0242         else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
0243             tbl_tw2865_common[0xcf] = 0x80;
0244     }
0245 
0246     for (i = 0; i < 0xff; i++) {
0247         /* Skip read only registers */
0248         switch (i) {
0249         case 0xb8 ... 0xc1:
0250         case 0xc4 ... 0xc7:
0251         case 0xfd:
0252             continue;
0253         }
0254         switch (i & ~0x30) {
0255         case 0x00:
0256         case 0x0c ... 0x0d:
0257             continue;
0258         }
0259 
0260         tw_write_and_verify(solo_dev, dev_addr, i,
0261                     tbl_tw2865_common[i]);
0262     }
0263 
0264     return 0;
0265 }
0266 
0267 static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr)
0268 {
0269     u8 tbl_tw2864_common[256];
0270     int i;
0271 
0272     if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
0273         memcpy(tbl_tw2864_common, tbl_tw2864_pal_template,
0274                sizeof(tbl_tw2864_common));
0275     else
0276         memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template,
0277                sizeof(tbl_tw2864_common));
0278 
0279     if (solo_dev->tw2865 == 0) {
0280         /* IRQ Mode */
0281         if (solo_dev->nr_chans == 4) {
0282             tbl_tw2864_common[0xd2] = 0x01;
0283             tbl_tw2864_common[0xcf] = 0x00;
0284         } else if (solo_dev->nr_chans == 8) {
0285             tbl_tw2864_common[0xd2] = 0x02;
0286             if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
0287                 tbl_tw2864_common[0xcf] = 0x43;
0288             else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
0289                 tbl_tw2864_common[0xcf] = 0x40;
0290         } else if (solo_dev->nr_chans == 16) {
0291             tbl_tw2864_common[0xd2] = 0x03;
0292             if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
0293                 tbl_tw2864_common[0xcf] = 0x43;
0294             else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
0295                 tbl_tw2864_common[0xcf] = 0x43;
0296             else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
0297                 tbl_tw2864_common[0xcf] = 0x43;
0298             else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
0299                 tbl_tw2864_common[0xcf] = 0x40;
0300         }
0301     } else {
0302         /* ALINK Mode. Assumes that the first tw28xx is a
0303          * 2865 and these are in cascade. */
0304         for (i = 0; i <= 4; i++)
0305             tbl_tw2864_common[0x08 | i << 4] = 0x12;
0306 
0307         if (solo_dev->nr_chans == 8) {
0308             tbl_tw2864_common[0xd2] = 0x02;
0309             if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
0310                 tbl_tw2864_common[0xcf] = 0x80;
0311         } else if (solo_dev->nr_chans == 16) {
0312             tbl_tw2864_common[0xd2] = 0x03;
0313             if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
0314                 tbl_tw2864_common[0xcf] = 0x83;
0315             else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
0316                 tbl_tw2864_common[0xcf] = 0x83;
0317             else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
0318                 tbl_tw2864_common[0xcf] = 0x80;
0319         }
0320     }
0321 
0322     for (i = 0; i < 0xff; i++) {
0323         /* Skip read only registers */
0324         switch (i) {
0325         case 0xb8 ... 0xc1:
0326         case 0xfd:
0327             continue;
0328         }
0329         switch (i & ~0x30) {
0330         case 0x00:
0331         case 0x0c:
0332         case 0x0d:
0333             continue;
0334         }
0335 
0336         tw_write_and_verify(solo_dev, dev_addr, i,
0337                     tbl_tw2864_common[i]);
0338     }
0339 
0340     return 0;
0341 }
0342 
0343 static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr)
0344 {
0345     u8 tbl_ntsc_tw2815_common[] = {
0346         0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
0347         0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
0348     };
0349 
0350     u8 tbl_pal_tw2815_common[] = {
0351         0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
0352         0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
0353     };
0354 
0355     u8 tbl_tw2815_sfr[] = {
0356         0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */
0357         0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
0358         0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */
0359         0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
0360         0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */
0361         0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
0362         0x88, 0x11, 0x00, 0x88, 0x88, 0x00,     /* 0x30 */
0363     };
0364     u8 *tbl_tw2815_common;
0365     int i;
0366     int ch;
0367 
0368     tbl_ntsc_tw2815_common[0x06] = 0;
0369 
0370     /* Horizontal Delay Control */
0371     tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff;
0372     tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8);
0373 
0374     /* Horizontal Active Control */
0375     tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff;
0376     tbl_ntsc_tw2815_common[0x06] |=
0377         ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2);
0378 
0379     /* Vertical Delay Control */
0380     tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff;
0381     tbl_ntsc_tw2815_common[0x06] |=
0382         ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4);
0383 
0384     /* Vertical Active Control */
0385     tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff;
0386     tbl_ntsc_tw2815_common[0x06] |=
0387         ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5);
0388 
0389     tbl_pal_tw2815_common[0x06] = 0;
0390 
0391     /* Horizontal Delay Control */
0392     tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff;
0393     tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8);
0394 
0395     /* Horizontal Active Control */
0396     tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff;
0397     tbl_pal_tw2815_common[0x06] |=
0398         ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2);
0399 
0400     /* Vertical Delay Control */
0401     tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff;
0402     tbl_pal_tw2815_common[0x06] |=
0403         ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4);
0404 
0405     /* Vertical Active Control */
0406     tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff;
0407     tbl_pal_tw2815_common[0x06] |=
0408         ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5);
0409 
0410     tbl_tw2815_common =
0411         (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
0412          tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
0413 
0414     /* Dual ITU-R BT.656 format */
0415     tbl_tw2815_common[0x0d] |= 0x04;
0416 
0417     /* Audio configuration */
0418     tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
0419 
0420     if (solo_dev->nr_chans == 4) {
0421         tbl_tw2815_sfr[0x63 - 0x40] |= 1;
0422         tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6;
0423     } else if (solo_dev->nr_chans == 8) {
0424         tbl_tw2815_sfr[0x63 - 0x40] |= 2;
0425         if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
0426             tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
0427         else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
0428             tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
0429     } else if (solo_dev->nr_chans == 16) {
0430         tbl_tw2815_sfr[0x63 - 0x40] |= 3;
0431         if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
0432             tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
0433         else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
0434             tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
0435         else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
0436             tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
0437         else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
0438             tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
0439     }
0440 
0441     /* Output mode of R_ADATM pin (0 mixing, 1 record) */
0442     /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
0443 
0444     /* 8KHz, used to be 16KHz, but changed for remote client compat */
0445     tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2;
0446     tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2;
0447 
0448     /* Playback of right channel */
0449     tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
0450 
0451     /* Reserved value (XXX ??) */
0452     tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
0453 
0454     /* Analog output gain and mix ratio playback on full */
0455     tbl_tw2815_sfr[0x70 - 0x40] |= 0xff;
0456     /* Select playback audio and mute all except */
0457     tbl_tw2815_sfr[0x71 - 0x40] |= 0x10;
0458     tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f;
0459 
0460     /* End of audio configuration */
0461 
0462     for (ch = 0; ch < 4; ch++) {
0463         tbl_tw2815_common[0x0d] &= ~3;
0464         switch (ch) {
0465         case 0:
0466             tbl_tw2815_common[0x0d] |= 0x21;
0467             break;
0468         case 1:
0469             tbl_tw2815_common[0x0d] |= 0x20;
0470             break;
0471         case 2:
0472             tbl_tw2815_common[0x0d] |= 0x23;
0473             break;
0474         case 3:
0475             tbl_tw2815_common[0x0d] |= 0x22;
0476             break;
0477         }
0478 
0479         for (i = 0; i < 0x0f; i++) {
0480             if (i == 0x00)
0481                 continue;   /* read-only */
0482             solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
0483                        dev_addr, (ch * 0x10) + i,
0484                        tbl_tw2815_common[i]);
0485         }
0486     }
0487 
0488     for (i = 0x40; i < 0x76; i++) {
0489         /* Skip read-only and nop registers */
0490         if (i == 0x40 || i == 0x59 || i == 0x5a ||
0491             i == 0x5d || i == 0x5e || i == 0x5f)
0492             continue;
0493 
0494         solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
0495                        tbl_tw2815_sfr[i - 0x40]);
0496     }
0497 
0498     return 0;
0499 }
0500 
0501 #define FIRST_ACTIVE_LINE   0x0008
0502 #define LAST_ACTIVE_LINE    0x0102
0503 
0504 static void saa712x_write_regs(struct solo_dev *dev, const u8 *vals,
0505         int start, int n)
0506 {
0507     for (; start < n; start++, vals++) {
0508         /* Skip read-only registers */
0509         switch (start) {
0510         /* case 0x00 ... 0x25: */
0511         case 0x2e ... 0x37:
0512         case 0x60:
0513         case 0x7d:
0514             continue;
0515         }
0516         solo_i2c_writebyte(dev, SOLO_I2C_SAA, 0x46, start, *vals);
0517     }
0518 }
0519 
0520 #define SAA712x_reg7c (0x80 | ((LAST_ACTIVE_LINE & 0x100) >> 2) \
0521         | ((FIRST_ACTIVE_LINE & 0x100) >> 4))
0522 
0523 static void saa712x_setup(struct solo_dev *dev)
0524 {
0525     const int reg_start = 0x26;
0526     static const u8 saa7128_regs_ntsc[] = {
0527     /* :0x26 */
0528         0x0d, 0x00,
0529     /* :0x28 */
0530         0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
0531     /* :0x2e XXX: read-only */
0532         0x00, 0x00,
0533         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0534     /* :0x38 */
0535         0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
0536     /* :0x40 */
0537         0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
0538         0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
0539     /* :0x50 */
0540         0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
0541         0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
0542     /* :0x60 */
0543         0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
0544         0x41, 0x88, 0x41, 0x52, 0xed, 0x10, 0x10, 0x00,
0545     /* :0x70 */
0546         0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
0547         0x00, 0x00, FIRST_ACTIVE_LINE, LAST_ACTIVE_LINE & 0xff,
0548         SAA712x_reg7c, 0x00, 0xff, 0xff,
0549     }, saa7128_regs_pal[] = {
0550     /* :0x26 */
0551         0x0d, 0x00,
0552     /* :0x28 */
0553         0xe1, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
0554     /* :0x2e XXX: read-only */
0555         0x00, 0x00,
0556         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0557     /* :0x38 */
0558         0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
0559     /* :0x40 */
0560         0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
0561         0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
0562     /* :0x50 */
0563         0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
0564         0x02, 0x80, 0x0f, 0x77, 0xa7, 0x67, 0x66, 0x2e,
0565     /* :0x60 */
0566         0x7b, 0x02, 0x35, 0xcb, 0x8a, 0x09, 0x2a, 0x77,
0567         0x41, 0x88, 0x41, 0x52, 0xf1, 0x10, 0x20, 0x00,
0568     /* :0x70 */
0569         0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
0570         0x00, 0x00, 0x12, 0x30,
0571         SAA712x_reg7c | 0x40, 0x00, 0xff, 0xff,
0572     };
0573 
0574     if (dev->video_type == SOLO_VO_FMT_TYPE_PAL)
0575         saa712x_write_regs(dev, saa7128_regs_pal, reg_start,
0576                 sizeof(saa7128_regs_pal));
0577     else
0578         saa712x_write_regs(dev, saa7128_regs_ntsc, reg_start,
0579                 sizeof(saa7128_regs_ntsc));
0580 }
0581 
0582 int solo_tw28_init(struct solo_dev *solo_dev)
0583 {
0584     int i;
0585     u8 value;
0586 
0587     solo_dev->tw28_cnt = 0;
0588 
0589     /* Detect techwell chip type(s) */
0590     for (i = 0; i < solo_dev->nr_chans / 4; i++) {
0591         value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
0592                       TW_CHIP_OFFSET_ADDR(i), 0xFF);
0593 
0594         switch (value >> 3) {
0595         case 0x18:
0596             solo_dev->tw2865 |= 1 << i;
0597             solo_dev->tw28_cnt++;
0598             break;
0599         case 0x0c:
0600         case 0x0d:
0601             solo_dev->tw2864 |= 1 << i;
0602             solo_dev->tw28_cnt++;
0603             break;
0604         default:
0605             value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
0606                           TW_CHIP_OFFSET_ADDR(i),
0607                           0x59);
0608             if ((value >> 3) == 0x04) {
0609                 solo_dev->tw2815 |= 1 << i;
0610                 solo_dev->tw28_cnt++;
0611             }
0612         }
0613     }
0614 
0615     if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) {
0616         dev_err(&solo_dev->pdev->dev,
0617             "Could not initialize any techwell chips\n");
0618         return -EINVAL;
0619     }
0620 
0621     saa712x_setup(solo_dev);
0622 
0623     for (i = 0; i < solo_dev->tw28_cnt; i++) {
0624         if ((solo_dev->tw2865 & (1 << i)))
0625             tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
0626         else if ((solo_dev->tw2864 & (1 << i)))
0627             tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
0628         else
0629             tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
0630     }
0631 
0632     return 0;
0633 }
0634 
0635 /*
0636  * We accessed the video status signal in the Techwell chip through
0637  * iic/i2c because the video status reported by register REG_VI_STATUS1
0638  * (address 0x012C) of the SOLO6010 chip doesn't give the correct video
0639  * status signal values.
0640  */
0641 int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch)
0642 {
0643     u8 val, chip_num;
0644 
0645     /* Get the right chip and on-chip channel */
0646     chip_num = ch / 4;
0647     ch %= 4;
0648 
0649     val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR,
0650               TW_AV_STAT_ADDR) & 0x0f;
0651 
0652     return val & (1 << ch) ? 1 : 0;
0653 }
0654 
0655 #if 0
0656 /* Status of audio from up to 4 techwell chips are combined into 1 variable.
0657  * See techwell datasheet for details. */
0658 u16 tw28_get_audio_status(struct solo_dev *solo_dev)
0659 {
0660     u8 val;
0661     u16 status = 0;
0662     int i;
0663 
0664     for (i = 0; i < solo_dev->tw28_cnt; i++) {
0665         val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR,
0666                    TW_AV_STAT_ADDR) & 0xf0) >> 4;
0667         status |= val << (i * 4);
0668     }
0669 
0670     return status;
0671 }
0672 #endif
0673 
0674 bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch)
0675 {
0676     return is_tw286x(solo_dev, ch / 4);
0677 }
0678 
0679 int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
0680               s32 val)
0681 {
0682     char sval;
0683     u8 chip_num;
0684 
0685     /* Get the right chip and on-chip channel */
0686     chip_num = ch / 4;
0687     ch %= 4;
0688 
0689     if (val > 255 || val < 0)
0690         return -ERANGE;
0691 
0692     switch (ctrl) {
0693     case V4L2_CID_SHARPNESS:
0694         /* Only 286x has sharpness */
0695         if (is_tw286x(solo_dev, chip_num)) {
0696             u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
0697                          TW_CHIP_OFFSET_ADDR(chip_num),
0698                          TW286x_SHARPNESS(chip_num));
0699             v &= 0xf0;
0700             v |= val;
0701             solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
0702                        TW_CHIP_OFFSET_ADDR(chip_num),
0703                        TW286x_SHARPNESS(chip_num), v);
0704         } else {
0705             return -EINVAL;
0706         }
0707         break;
0708 
0709     case V4L2_CID_HUE:
0710         if (is_tw286x(solo_dev, chip_num))
0711             sval = val - 128;
0712         else
0713             sval = (char)val;
0714         tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
0715                  TW_HUE_ADDR(ch), sval);
0716 
0717         break;
0718 
0719     case V4L2_CID_SATURATION:
0720         /* 286x chips have a U and V component for saturation */
0721         if (is_tw286x(solo_dev, chip_num)) {
0722             solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
0723                        TW_CHIP_OFFSET_ADDR(chip_num),
0724                        TW286x_SATURATIONU_ADDR(ch), val);
0725         }
0726         tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
0727                  TW_SATURATION_ADDR(ch), val);
0728 
0729         break;
0730 
0731     case V4L2_CID_CONTRAST:
0732         tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
0733                  TW_CONTRAST_ADDR(ch), val);
0734         break;
0735 
0736     case V4L2_CID_BRIGHTNESS:
0737         if (is_tw286x(solo_dev, chip_num))
0738             sval = val - 128;
0739         else
0740             sval = (char)val;
0741         tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
0742                  TW_BRIGHTNESS_ADDR(ch), sval);
0743 
0744         break;
0745     default:
0746         return -EINVAL;
0747     }
0748 
0749     return 0;
0750 }
0751 
0752 int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
0753               s32 *val)
0754 {
0755     u8 rval, chip_num;
0756 
0757     /* Get the right chip and on-chip channel */
0758     chip_num = ch / 4;
0759     ch %= 4;
0760 
0761     switch (ctrl) {
0762     case V4L2_CID_SHARPNESS:
0763         /* Only 286x has sharpness */
0764         if (is_tw286x(solo_dev, chip_num)) {
0765             rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
0766                          TW_CHIP_OFFSET_ADDR(chip_num),
0767                          TW286x_SHARPNESS(chip_num));
0768             *val = rval & 0x0f;
0769         } else
0770             *val = 0;
0771         break;
0772     case V4L2_CID_HUE:
0773         rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
0774                    TW_HUE_ADDR(ch));
0775         if (is_tw286x(solo_dev, chip_num))
0776             *val = (s32)((char)rval) + 128;
0777         else
0778             *val = rval;
0779         break;
0780     case V4L2_CID_SATURATION:
0781         *val = tw_readbyte(solo_dev, chip_num,
0782                    TW286x_SATURATIONU_ADDR(ch),
0783                    TW_SATURATION_ADDR(ch));
0784         break;
0785     case V4L2_CID_CONTRAST:
0786         *val = tw_readbyte(solo_dev, chip_num,
0787                    TW286x_CONTRAST_ADDR(ch),
0788                    TW_CONTRAST_ADDR(ch));
0789         break;
0790     case V4L2_CID_BRIGHTNESS:
0791         rval = tw_readbyte(solo_dev, chip_num,
0792                    TW286x_BRIGHTNESS_ADDR(ch),
0793                    TW_BRIGHTNESS_ADDR(ch));
0794         if (is_tw286x(solo_dev, chip_num))
0795             *val = (s32)((char)rval) + 128;
0796         else
0797             *val = rval;
0798         break;
0799     default:
0800         return -EINVAL;
0801     }
0802 
0803     return 0;
0804 }
0805 
0806 #if 0
0807 /*
0808  * For audio output volume, the output channel is only 1. In this case we
0809  * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
0810  * is the base address of the techwell chip.
0811  */
0812 void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val)
0813 {
0814     unsigned int val;
0815     unsigned int chip_num;
0816 
0817     chip_num = (solo_dev->nr_chans - 1) / 4;
0818 
0819     val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
0820               TW_AUDIO_OUTPUT_VOL_ADDR);
0821 
0822     u_val = (val & 0x0f) | (u_val << 4);
0823 
0824     tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
0825              TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
0826 }
0827 #endif
0828 
0829 u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch)
0830 {
0831     u8 val;
0832     u8 chip_num;
0833 
0834     /* Get the right chip and on-chip channel */
0835     chip_num = ch / 4;
0836     ch %= 4;
0837 
0838     val = tw_readbyte(solo_dev, chip_num,
0839               TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
0840               TW_AUDIO_INPUT_GAIN_ADDR(ch));
0841 
0842     return (ch % 2) ? (val >> 4) : (val & 0x0f);
0843 }
0844 
0845 void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val)
0846 {
0847     u8 old_val;
0848     u8 chip_num;
0849 
0850     /* Get the right chip and on-chip channel */
0851     chip_num = ch / 4;
0852     ch %= 4;
0853 
0854     old_val = tw_readbyte(solo_dev, chip_num,
0855                   TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
0856                   TW_AUDIO_INPUT_GAIN_ADDR(ch));
0857 
0858     val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
0859         ((ch % 2) ? (val << 4) : val);
0860 
0861     tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
0862              TW_AUDIO_INPUT_GAIN_ADDR(ch), val);
0863 }