18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Original author:
68c2ecf20Sopenharmony_ci * Ben Collins <bcollins@ubuntu.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Additional work by:
98c2ecf20Sopenharmony_ci * John Brooks <john.brooks@bluecherry.net>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "solo6x10.h"
168c2ecf20Sopenharmony_ci#include "solo6x10-tw28.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define DEFAULT_HDELAY_NTSC		(32 - 8)
198c2ecf20Sopenharmony_ci#define DEFAULT_HACTIVE_NTSC		(720 + 16)
208c2ecf20Sopenharmony_ci#define DEFAULT_VDELAY_NTSC		(7 - 2)
218c2ecf20Sopenharmony_ci#define DEFAULT_VACTIVE_NTSC		(240 + 4)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define DEFAULT_HDELAY_PAL		(32 + 4)
248c2ecf20Sopenharmony_ci#define DEFAULT_HACTIVE_PAL		(864-DEFAULT_HDELAY_PAL)
258c2ecf20Sopenharmony_ci#define DEFAULT_VDELAY_PAL		(6)
268c2ecf20Sopenharmony_ci#define DEFAULT_VACTIVE_PAL		(312-DEFAULT_VDELAY_PAL)
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic const u8 tbl_tw2864_ntsc_template[] = {
308c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
318c2ecf20Sopenharmony_ci	0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
328c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
338c2ecf20Sopenharmony_ci	0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
348c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
358c2ecf20Sopenharmony_ci	0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
368c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
378c2ecf20Sopenharmony_ci	0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
388c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
398c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
408c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
418c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
438c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
448c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
458c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
468c2ecf20Sopenharmony_ci	0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
478c2ecf20Sopenharmony_ci	0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
488c2ecf20Sopenharmony_ci	0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
498c2ecf20Sopenharmony_ci	0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01,
508c2ecf20Sopenharmony_ci	0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
518c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
528c2ecf20Sopenharmony_ci	0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
538c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
548c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
558c2ecf20Sopenharmony_ci	0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
568c2ecf20Sopenharmony_ci	0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
578c2ecf20Sopenharmony_ci	0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
588c2ecf20Sopenharmony_ci	0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
598c2ecf20Sopenharmony_ci	0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
608c2ecf20Sopenharmony_ci	0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
618c2ecf20Sopenharmony_ci	0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const u8 tbl_tw2864_pal_template[] = {
658c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
668c2ecf20Sopenharmony_ci	0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
678c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
688c2ecf20Sopenharmony_ci	0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
698c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
708c2ecf20Sopenharmony_ci	0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
718c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
728c2ecf20Sopenharmony_ci	0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
738c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
748c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
758c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
768c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
778c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
788c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
798c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
808c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
818c2ecf20Sopenharmony_ci	0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
828c2ecf20Sopenharmony_ci	0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
838c2ecf20Sopenharmony_ci	0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
848c2ecf20Sopenharmony_ci	0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01,
858c2ecf20Sopenharmony_ci	0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
868c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
878c2ecf20Sopenharmony_ci	0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
888c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
898c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
908c2ecf20Sopenharmony_ci	0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
918c2ecf20Sopenharmony_ci	0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
928c2ecf20Sopenharmony_ci	0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
938c2ecf20Sopenharmony_ci	0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
948c2ecf20Sopenharmony_ci	0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
958c2ecf20Sopenharmony_ci	0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */
968c2ecf20Sopenharmony_ci	0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic const u8 tbl_tw2865_ntsc_template[] = {
1008c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
1018c2ecf20Sopenharmony_ci	0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
1028c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
1038c2ecf20Sopenharmony_ci	0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
1048c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
1058c2ecf20Sopenharmony_ci	0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
1068c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
1078c2ecf20Sopenharmony_ci	0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
1088c2ecf20Sopenharmony_ci	0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */
1098c2ecf20Sopenharmony_ci	0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
1108c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
1118c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1128c2ecf20Sopenharmony_ci	0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
1138c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
1148c2ecf20Sopenharmony_ci	0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
1158c2ecf20Sopenharmony_ci	0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
1168c2ecf20Sopenharmony_ci	0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
1178c2ecf20Sopenharmony_ci	0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
1188c2ecf20Sopenharmony_ci	0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
1198c2ecf20Sopenharmony_ci	0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
1208c2ecf20Sopenharmony_ci	0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */
1218c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
1228c2ecf20Sopenharmony_ci	0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
1238c2ecf20Sopenharmony_ci	0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8,
1248c2ecf20Sopenharmony_ci	0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
1258c2ecf20Sopenharmony_ci	0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
1268c2ecf20Sopenharmony_ci	0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
1278c2ecf20Sopenharmony_ci	0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
1288c2ecf20Sopenharmony_ci	0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
1298c2ecf20Sopenharmony_ci	0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
1308c2ecf20Sopenharmony_ci	0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
1318c2ecf20Sopenharmony_ci	0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic const u8 tbl_tw2865_pal_template[] = {
1358c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
1368c2ecf20Sopenharmony_ci	0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
1378c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
1388c2ecf20Sopenharmony_ci	0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
1398c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
1408c2ecf20Sopenharmony_ci	0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
1418c2ecf20Sopenharmony_ci	0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
1428c2ecf20Sopenharmony_ci	0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
1438c2ecf20Sopenharmony_ci	0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */
1448c2ecf20Sopenharmony_ci	0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
1458c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
1468c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1478c2ecf20Sopenharmony_ci	0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
1488c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
1498c2ecf20Sopenharmony_ci	0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
1508c2ecf20Sopenharmony_ci	0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
1518c2ecf20Sopenharmony_ci	0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
1528c2ecf20Sopenharmony_ci	0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
1538c2ecf20Sopenharmony_ci	0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
1548c2ecf20Sopenharmony_ci	0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
1558c2ecf20Sopenharmony_ci	0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */
1568c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
1578c2ecf20Sopenharmony_ci	0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
1588c2ecf20Sopenharmony_ci	0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8,
1598c2ecf20Sopenharmony_ci	0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
1608c2ecf20Sopenharmony_ci	0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
1618c2ecf20Sopenharmony_ci	0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
1628c2ecf20Sopenharmony_ci	0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
1638c2ecf20Sopenharmony_ci	0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
1648c2ecf20Sopenharmony_ci	0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
1658c2ecf20Sopenharmony_ci	0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */
1668c2ecf20Sopenharmony_ci	0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off,
1728c2ecf20Sopenharmony_ci		      u8 tw_off)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	if (is_tw286x(solo_dev, chip_id))
1758c2ecf20Sopenharmony_ci		return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
1768c2ecf20Sopenharmony_ci					 TW_CHIP_OFFSET_ADDR(chip_id),
1778c2ecf20Sopenharmony_ci					 tw6x_off);
1788c2ecf20Sopenharmony_ci	else
1798c2ecf20Sopenharmony_ci		return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
1808c2ecf20Sopenharmony_ci					 TW_CHIP_OFFSET_ADDR(chip_id),
1818c2ecf20Sopenharmony_ci					 tw_off);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void tw_writebyte(struct solo_dev *solo_dev, int chip_id,
1858c2ecf20Sopenharmony_ci			 u8 tw6x_off, u8 tw_off, u8 val)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	if (is_tw286x(solo_dev, chip_id))
1888c2ecf20Sopenharmony_ci		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
1898c2ecf20Sopenharmony_ci				   TW_CHIP_OFFSET_ADDR(chip_id),
1908c2ecf20Sopenharmony_ci				   tw6x_off, val);
1918c2ecf20Sopenharmony_ci	else
1928c2ecf20Sopenharmony_ci		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
1938c2ecf20Sopenharmony_ci				   TW_CHIP_OFFSET_ADDR(chip_id),
1948c2ecf20Sopenharmony_ci				   tw_off, val);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
1988c2ecf20Sopenharmony_ci				u8 val)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	int i;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++) {
2038c2ecf20Sopenharmony_ci		u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		if (rval == val)
2068c2ecf20Sopenharmony_ci			return;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
2098c2ecf20Sopenharmony_ci		msleep_interruptible(1);
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/*	printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
2138c2ecf20Sopenharmony_ci/*		addr, off, val); */
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	u8 tbl_tw2865_common[256];
2198c2ecf20Sopenharmony_ci	int i;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
2228c2ecf20Sopenharmony_ci		memcpy(tbl_tw2865_common, tbl_tw2865_pal_template,
2238c2ecf20Sopenharmony_ci		       sizeof(tbl_tw2865_common));
2248c2ecf20Sopenharmony_ci	else
2258c2ecf20Sopenharmony_ci		memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
2268c2ecf20Sopenharmony_ci		       sizeof(tbl_tw2865_common));
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* ALINK Mode */
2298c2ecf20Sopenharmony_ci	if (solo_dev->nr_chans == 4) {
2308c2ecf20Sopenharmony_ci		tbl_tw2865_common[0xd2] = 0x01;
2318c2ecf20Sopenharmony_ci		tbl_tw2865_common[0xcf] = 0x00;
2328c2ecf20Sopenharmony_ci	} else if (solo_dev->nr_chans == 8) {
2338c2ecf20Sopenharmony_ci		tbl_tw2865_common[0xd2] = 0x02;
2348c2ecf20Sopenharmony_ci		if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
2358c2ecf20Sopenharmony_ci			tbl_tw2865_common[0xcf] = 0x80;
2368c2ecf20Sopenharmony_ci	} else if (solo_dev->nr_chans == 16) {
2378c2ecf20Sopenharmony_ci		tbl_tw2865_common[0xd2] = 0x03;
2388c2ecf20Sopenharmony_ci		if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
2398c2ecf20Sopenharmony_ci			tbl_tw2865_common[0xcf] = 0x83;
2408c2ecf20Sopenharmony_ci		else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
2418c2ecf20Sopenharmony_ci			tbl_tw2865_common[0xcf] = 0x83;
2428c2ecf20Sopenharmony_ci		else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
2438c2ecf20Sopenharmony_ci			tbl_tw2865_common[0xcf] = 0x80;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	for (i = 0; i < 0xff; i++) {
2478c2ecf20Sopenharmony_ci		/* Skip read only registers */
2488c2ecf20Sopenharmony_ci		switch (i) {
2498c2ecf20Sopenharmony_ci		case 0xb8 ... 0xc1:
2508c2ecf20Sopenharmony_ci		case 0xc4 ... 0xc7:
2518c2ecf20Sopenharmony_ci		case 0xfd:
2528c2ecf20Sopenharmony_ci			continue;
2538c2ecf20Sopenharmony_ci		}
2548c2ecf20Sopenharmony_ci		switch (i & ~0x30) {
2558c2ecf20Sopenharmony_ci		case 0x00:
2568c2ecf20Sopenharmony_ci		case 0x0c ... 0x0d:
2578c2ecf20Sopenharmony_ci			continue;
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		tw_write_and_verify(solo_dev, dev_addr, i,
2618c2ecf20Sopenharmony_ci				    tbl_tw2865_common[i]);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return 0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	u8 tbl_tw2864_common[256];
2708c2ecf20Sopenharmony_ci	int i;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
2738c2ecf20Sopenharmony_ci		memcpy(tbl_tw2864_common, tbl_tw2864_pal_template,
2748c2ecf20Sopenharmony_ci		       sizeof(tbl_tw2864_common));
2758c2ecf20Sopenharmony_ci	else
2768c2ecf20Sopenharmony_ci		memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template,
2778c2ecf20Sopenharmony_ci		       sizeof(tbl_tw2864_common));
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (solo_dev->tw2865 == 0) {
2808c2ecf20Sopenharmony_ci		/* IRQ Mode */
2818c2ecf20Sopenharmony_ci		if (solo_dev->nr_chans == 4) {
2828c2ecf20Sopenharmony_ci			tbl_tw2864_common[0xd2] = 0x01;
2838c2ecf20Sopenharmony_ci			tbl_tw2864_common[0xcf] = 0x00;
2848c2ecf20Sopenharmony_ci		} else if (solo_dev->nr_chans == 8) {
2858c2ecf20Sopenharmony_ci			tbl_tw2864_common[0xd2] = 0x02;
2868c2ecf20Sopenharmony_ci			if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
2878c2ecf20Sopenharmony_ci				tbl_tw2864_common[0xcf] = 0x43;
2888c2ecf20Sopenharmony_ci			else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
2898c2ecf20Sopenharmony_ci				tbl_tw2864_common[0xcf] = 0x40;
2908c2ecf20Sopenharmony_ci		} else if (solo_dev->nr_chans == 16) {
2918c2ecf20Sopenharmony_ci			tbl_tw2864_common[0xd2] = 0x03;
2928c2ecf20Sopenharmony_ci			if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
2938c2ecf20Sopenharmony_ci				tbl_tw2864_common[0xcf] = 0x43;
2948c2ecf20Sopenharmony_ci			else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
2958c2ecf20Sopenharmony_ci				tbl_tw2864_common[0xcf] = 0x43;
2968c2ecf20Sopenharmony_ci			else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
2978c2ecf20Sopenharmony_ci				tbl_tw2864_common[0xcf] = 0x43;
2988c2ecf20Sopenharmony_ci			else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
2998c2ecf20Sopenharmony_ci				tbl_tw2864_common[0xcf] = 0x40;
3008c2ecf20Sopenharmony_ci		}
3018c2ecf20Sopenharmony_ci	} else {
3028c2ecf20Sopenharmony_ci		/* ALINK Mode. Assumes that the first tw28xx is a
3038c2ecf20Sopenharmony_ci		 * 2865 and these are in cascade. */
3048c2ecf20Sopenharmony_ci		for (i = 0; i <= 4; i++)
3058c2ecf20Sopenharmony_ci			tbl_tw2864_common[0x08 | i << 4] = 0x12;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		if (solo_dev->nr_chans == 8) {
3088c2ecf20Sopenharmony_ci			tbl_tw2864_common[0xd2] = 0x02;
3098c2ecf20Sopenharmony_ci			if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
3108c2ecf20Sopenharmony_ci				tbl_tw2864_common[0xcf] = 0x80;
3118c2ecf20Sopenharmony_ci		} else if (solo_dev->nr_chans == 16) {
3128c2ecf20Sopenharmony_ci			tbl_tw2864_common[0xd2] = 0x03;
3138c2ecf20Sopenharmony_ci			if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
3148c2ecf20Sopenharmony_ci				tbl_tw2864_common[0xcf] = 0x83;
3158c2ecf20Sopenharmony_ci			else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
3168c2ecf20Sopenharmony_ci				tbl_tw2864_common[0xcf] = 0x83;
3178c2ecf20Sopenharmony_ci			else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
3188c2ecf20Sopenharmony_ci				tbl_tw2864_common[0xcf] = 0x80;
3198c2ecf20Sopenharmony_ci		}
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	for (i = 0; i < 0xff; i++) {
3238c2ecf20Sopenharmony_ci		/* Skip read only registers */
3248c2ecf20Sopenharmony_ci		switch (i) {
3258c2ecf20Sopenharmony_ci		case 0xb8 ... 0xc1:
3268c2ecf20Sopenharmony_ci		case 0xfd:
3278c2ecf20Sopenharmony_ci			continue;
3288c2ecf20Sopenharmony_ci		}
3298c2ecf20Sopenharmony_ci		switch (i & ~0x30) {
3308c2ecf20Sopenharmony_ci		case 0x00:
3318c2ecf20Sopenharmony_ci		case 0x0c:
3328c2ecf20Sopenharmony_ci		case 0x0d:
3338c2ecf20Sopenharmony_ci			continue;
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		tw_write_and_verify(solo_dev, dev_addr, i,
3378c2ecf20Sopenharmony_ci				    tbl_tw2864_common[i]);
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	return 0;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	u8 tbl_ntsc_tw2815_common[] = {
3468c2ecf20Sopenharmony_ci		0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
3478c2ecf20Sopenharmony_ci		0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
3488c2ecf20Sopenharmony_ci	};
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	u8 tbl_pal_tw2815_common[] = {
3518c2ecf20Sopenharmony_ci		0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
3528c2ecf20Sopenharmony_ci		0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
3538c2ecf20Sopenharmony_ci	};
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	u8 tbl_tw2815_sfr[] = {
3568c2ecf20Sopenharmony_ci		0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */
3578c2ecf20Sopenharmony_ci		0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
3588c2ecf20Sopenharmony_ci		0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */
3598c2ecf20Sopenharmony_ci		0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
3608c2ecf20Sopenharmony_ci		0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */
3618c2ecf20Sopenharmony_ci		0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
3628c2ecf20Sopenharmony_ci		0x88, 0x11, 0x00, 0x88, 0x88, 0x00,		/* 0x30 */
3638c2ecf20Sopenharmony_ci	};
3648c2ecf20Sopenharmony_ci	u8 *tbl_tw2815_common;
3658c2ecf20Sopenharmony_ci	int i;
3668c2ecf20Sopenharmony_ci	int ch;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	tbl_ntsc_tw2815_common[0x06] = 0;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* Horizontal Delay Control */
3718c2ecf20Sopenharmony_ci	tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff;
3728c2ecf20Sopenharmony_ci	tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* Horizontal Active Control */
3758c2ecf20Sopenharmony_ci	tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff;
3768c2ecf20Sopenharmony_ci	tbl_ntsc_tw2815_common[0x06] |=
3778c2ecf20Sopenharmony_ci		((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* Vertical Delay Control */
3808c2ecf20Sopenharmony_ci	tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff;
3818c2ecf20Sopenharmony_ci	tbl_ntsc_tw2815_common[0x06] |=
3828c2ecf20Sopenharmony_ci		((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* Vertical Active Control */
3858c2ecf20Sopenharmony_ci	tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff;
3868c2ecf20Sopenharmony_ci	tbl_ntsc_tw2815_common[0x06] |=
3878c2ecf20Sopenharmony_ci		((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	tbl_pal_tw2815_common[0x06] = 0;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* Horizontal Delay Control */
3928c2ecf20Sopenharmony_ci	tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff;
3938c2ecf20Sopenharmony_ci	tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/* Horizontal Active Control */
3968c2ecf20Sopenharmony_ci	tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff;
3978c2ecf20Sopenharmony_ci	tbl_pal_tw2815_common[0x06] |=
3988c2ecf20Sopenharmony_ci		((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* Vertical Delay Control */
4018c2ecf20Sopenharmony_ci	tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff;
4028c2ecf20Sopenharmony_ci	tbl_pal_tw2815_common[0x06] |=
4038c2ecf20Sopenharmony_ci		((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/* Vertical Active Control */
4068c2ecf20Sopenharmony_ci	tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff;
4078c2ecf20Sopenharmony_ci	tbl_pal_tw2815_common[0x06] |=
4088c2ecf20Sopenharmony_ci		((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	tbl_tw2815_common =
4118c2ecf20Sopenharmony_ci	    (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
4128c2ecf20Sopenharmony_ci	     tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* Dual ITU-R BT.656 format */
4158c2ecf20Sopenharmony_ci	tbl_tw2815_common[0x0d] |= 0x04;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* Audio configuration */
4188c2ecf20Sopenharmony_ci	tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (solo_dev->nr_chans == 4) {
4218c2ecf20Sopenharmony_ci		tbl_tw2815_sfr[0x63 - 0x40] |= 1;
4228c2ecf20Sopenharmony_ci		tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6;
4238c2ecf20Sopenharmony_ci	} else if (solo_dev->nr_chans == 8) {
4248c2ecf20Sopenharmony_ci		tbl_tw2815_sfr[0x63 - 0x40] |= 2;
4258c2ecf20Sopenharmony_ci		if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
4268c2ecf20Sopenharmony_ci			tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
4278c2ecf20Sopenharmony_ci		else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
4288c2ecf20Sopenharmony_ci			tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
4298c2ecf20Sopenharmony_ci	} else if (solo_dev->nr_chans == 16) {
4308c2ecf20Sopenharmony_ci		tbl_tw2815_sfr[0x63 - 0x40] |= 3;
4318c2ecf20Sopenharmony_ci		if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
4328c2ecf20Sopenharmony_ci			tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
4338c2ecf20Sopenharmony_ci		else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
4348c2ecf20Sopenharmony_ci			tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
4358c2ecf20Sopenharmony_ci		else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
4368c2ecf20Sopenharmony_ci			tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
4378c2ecf20Sopenharmony_ci		else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
4388c2ecf20Sopenharmony_ci			tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* Output mode of R_ADATM pin (0 mixing, 1 record) */
4428c2ecf20Sopenharmony_ci	/* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/* 8KHz, used to be 16KHz, but changed for remote client compat */
4458c2ecf20Sopenharmony_ci	tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2;
4468c2ecf20Sopenharmony_ci	tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* Playback of right channel */
4498c2ecf20Sopenharmony_ci	tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	/* Reserved value (XXX ??) */
4528c2ecf20Sopenharmony_ci	tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* Analog output gain and mix ratio playback on full */
4558c2ecf20Sopenharmony_ci	tbl_tw2815_sfr[0x70 - 0x40] |= 0xff;
4568c2ecf20Sopenharmony_ci	/* Select playback audio and mute all except */
4578c2ecf20Sopenharmony_ci	tbl_tw2815_sfr[0x71 - 0x40] |= 0x10;
4588c2ecf20Sopenharmony_ci	tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* End of audio configuration */
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	for (ch = 0; ch < 4; ch++) {
4638c2ecf20Sopenharmony_ci		tbl_tw2815_common[0x0d] &= ~3;
4648c2ecf20Sopenharmony_ci		switch (ch) {
4658c2ecf20Sopenharmony_ci		case 0:
4668c2ecf20Sopenharmony_ci			tbl_tw2815_common[0x0d] |= 0x21;
4678c2ecf20Sopenharmony_ci			break;
4688c2ecf20Sopenharmony_ci		case 1:
4698c2ecf20Sopenharmony_ci			tbl_tw2815_common[0x0d] |= 0x20;
4708c2ecf20Sopenharmony_ci			break;
4718c2ecf20Sopenharmony_ci		case 2:
4728c2ecf20Sopenharmony_ci			tbl_tw2815_common[0x0d] |= 0x23;
4738c2ecf20Sopenharmony_ci			break;
4748c2ecf20Sopenharmony_ci		case 3:
4758c2ecf20Sopenharmony_ci			tbl_tw2815_common[0x0d] |= 0x22;
4768c2ecf20Sopenharmony_ci			break;
4778c2ecf20Sopenharmony_ci		}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		for (i = 0; i < 0x0f; i++) {
4808c2ecf20Sopenharmony_ci			if (i == 0x00)
4818c2ecf20Sopenharmony_ci				continue;	/* read-only */
4828c2ecf20Sopenharmony_ci			solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
4838c2ecf20Sopenharmony_ci					   dev_addr, (ch * 0x10) + i,
4848c2ecf20Sopenharmony_ci					   tbl_tw2815_common[i]);
4858c2ecf20Sopenharmony_ci		}
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	for (i = 0x40; i < 0x76; i++) {
4898c2ecf20Sopenharmony_ci		/* Skip read-only and nop registers */
4908c2ecf20Sopenharmony_ci		if (i == 0x40 || i == 0x59 || i == 0x5a ||
4918c2ecf20Sopenharmony_ci		    i == 0x5d || i == 0x5e || i == 0x5f)
4928c2ecf20Sopenharmony_ci			continue;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
4958c2ecf20Sopenharmony_ci				       tbl_tw2815_sfr[i - 0x40]);
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	return 0;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci#define FIRST_ACTIVE_LINE	0x0008
5028c2ecf20Sopenharmony_ci#define LAST_ACTIVE_LINE	0x0102
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic void saa712x_write_regs(struct solo_dev *dev, const u8 *vals,
5058c2ecf20Sopenharmony_ci		int start, int n)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	for (; start < n; start++, vals++) {
5088c2ecf20Sopenharmony_ci		/* Skip read-only registers */
5098c2ecf20Sopenharmony_ci		switch (start) {
5108c2ecf20Sopenharmony_ci		/* case 0x00 ... 0x25: */
5118c2ecf20Sopenharmony_ci		case 0x2e ... 0x37:
5128c2ecf20Sopenharmony_ci		case 0x60:
5138c2ecf20Sopenharmony_ci		case 0x7d:
5148c2ecf20Sopenharmony_ci			continue;
5158c2ecf20Sopenharmony_ci		}
5168c2ecf20Sopenharmony_ci		solo_i2c_writebyte(dev, SOLO_I2C_SAA, 0x46, start, *vals);
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci#define SAA712x_reg7c (0x80 | ((LAST_ACTIVE_LINE & 0x100) >> 2) \
5218c2ecf20Sopenharmony_ci		| ((FIRST_ACTIVE_LINE & 0x100) >> 4))
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic void saa712x_setup(struct solo_dev *dev)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	const int reg_start = 0x26;
5268c2ecf20Sopenharmony_ci	static const u8 saa7128_regs_ntsc[] = {
5278c2ecf20Sopenharmony_ci	/* :0x26 */
5288c2ecf20Sopenharmony_ci		0x0d, 0x00,
5298c2ecf20Sopenharmony_ci	/* :0x28 */
5308c2ecf20Sopenharmony_ci		0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
5318c2ecf20Sopenharmony_ci	/* :0x2e XXX: read-only */
5328c2ecf20Sopenharmony_ci		0x00, 0x00,
5338c2ecf20Sopenharmony_ci		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5348c2ecf20Sopenharmony_ci	/* :0x38 */
5358c2ecf20Sopenharmony_ci		0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
5368c2ecf20Sopenharmony_ci	/* :0x40 */
5378c2ecf20Sopenharmony_ci		0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
5388c2ecf20Sopenharmony_ci		0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
5398c2ecf20Sopenharmony_ci	/* :0x50 */
5408c2ecf20Sopenharmony_ci		0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
5418c2ecf20Sopenharmony_ci		0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
5428c2ecf20Sopenharmony_ci	/* :0x60 */
5438c2ecf20Sopenharmony_ci		0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
5448c2ecf20Sopenharmony_ci		0x41, 0x88, 0x41, 0x52, 0xed, 0x10, 0x10, 0x00,
5458c2ecf20Sopenharmony_ci	/* :0x70 */
5468c2ecf20Sopenharmony_ci		0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
5478c2ecf20Sopenharmony_ci		0x00, 0x00, FIRST_ACTIVE_LINE, LAST_ACTIVE_LINE & 0xff,
5488c2ecf20Sopenharmony_ci		SAA712x_reg7c, 0x00, 0xff, 0xff,
5498c2ecf20Sopenharmony_ci	}, saa7128_regs_pal[] = {
5508c2ecf20Sopenharmony_ci	/* :0x26 */
5518c2ecf20Sopenharmony_ci		0x0d, 0x00,
5528c2ecf20Sopenharmony_ci	/* :0x28 */
5538c2ecf20Sopenharmony_ci		0xe1, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
5548c2ecf20Sopenharmony_ci	/* :0x2e XXX: read-only */
5558c2ecf20Sopenharmony_ci		0x00, 0x00,
5568c2ecf20Sopenharmony_ci		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5578c2ecf20Sopenharmony_ci	/* :0x38 */
5588c2ecf20Sopenharmony_ci		0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
5598c2ecf20Sopenharmony_ci	/* :0x40 */
5608c2ecf20Sopenharmony_ci		0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
5618c2ecf20Sopenharmony_ci		0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
5628c2ecf20Sopenharmony_ci	/* :0x50 */
5638c2ecf20Sopenharmony_ci		0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
5648c2ecf20Sopenharmony_ci		0x02, 0x80, 0x0f, 0x77, 0xa7, 0x67, 0x66, 0x2e,
5658c2ecf20Sopenharmony_ci	/* :0x60 */
5668c2ecf20Sopenharmony_ci		0x7b, 0x02, 0x35, 0xcb, 0x8a, 0x09, 0x2a, 0x77,
5678c2ecf20Sopenharmony_ci		0x41, 0x88, 0x41, 0x52, 0xf1, 0x10, 0x20, 0x00,
5688c2ecf20Sopenharmony_ci	/* :0x70 */
5698c2ecf20Sopenharmony_ci		0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
5708c2ecf20Sopenharmony_ci		0x00, 0x00, 0x12, 0x30,
5718c2ecf20Sopenharmony_ci		SAA712x_reg7c | 0x40, 0x00, 0xff, 0xff,
5728c2ecf20Sopenharmony_ci	};
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (dev->video_type == SOLO_VO_FMT_TYPE_PAL)
5758c2ecf20Sopenharmony_ci		saa712x_write_regs(dev, saa7128_regs_pal, reg_start,
5768c2ecf20Sopenharmony_ci				sizeof(saa7128_regs_pal));
5778c2ecf20Sopenharmony_ci	else
5788c2ecf20Sopenharmony_ci		saa712x_write_regs(dev, saa7128_regs_ntsc, reg_start,
5798c2ecf20Sopenharmony_ci				sizeof(saa7128_regs_ntsc));
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ciint solo_tw28_init(struct solo_dev *solo_dev)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	int i;
5858c2ecf20Sopenharmony_ci	u8 value;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	solo_dev->tw28_cnt = 0;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* Detect techwell chip type(s) */
5908c2ecf20Sopenharmony_ci	for (i = 0; i < solo_dev->nr_chans / 4; i++) {
5918c2ecf20Sopenharmony_ci		value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
5928c2ecf20Sopenharmony_ci					  TW_CHIP_OFFSET_ADDR(i), 0xFF);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		switch (value >> 3) {
5958c2ecf20Sopenharmony_ci		case 0x18:
5968c2ecf20Sopenharmony_ci			solo_dev->tw2865 |= 1 << i;
5978c2ecf20Sopenharmony_ci			solo_dev->tw28_cnt++;
5988c2ecf20Sopenharmony_ci			break;
5998c2ecf20Sopenharmony_ci		case 0x0c:
6008c2ecf20Sopenharmony_ci		case 0x0d:
6018c2ecf20Sopenharmony_ci			solo_dev->tw2864 |= 1 << i;
6028c2ecf20Sopenharmony_ci			solo_dev->tw28_cnt++;
6038c2ecf20Sopenharmony_ci			break;
6048c2ecf20Sopenharmony_ci		default:
6058c2ecf20Sopenharmony_ci			value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
6068c2ecf20Sopenharmony_ci						  TW_CHIP_OFFSET_ADDR(i),
6078c2ecf20Sopenharmony_ci						  0x59);
6088c2ecf20Sopenharmony_ci			if ((value >> 3) == 0x04) {
6098c2ecf20Sopenharmony_ci				solo_dev->tw2815 |= 1 << i;
6108c2ecf20Sopenharmony_ci				solo_dev->tw28_cnt++;
6118c2ecf20Sopenharmony_ci			}
6128c2ecf20Sopenharmony_ci		}
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) {
6168c2ecf20Sopenharmony_ci		dev_err(&solo_dev->pdev->dev,
6178c2ecf20Sopenharmony_ci			"Could not initialize any techwell chips\n");
6188c2ecf20Sopenharmony_ci		return -EINVAL;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	saa712x_setup(solo_dev);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	for (i = 0; i < solo_dev->tw28_cnt; i++) {
6248c2ecf20Sopenharmony_ci		if ((solo_dev->tw2865 & (1 << i)))
6258c2ecf20Sopenharmony_ci			tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
6268c2ecf20Sopenharmony_ci		else if ((solo_dev->tw2864 & (1 << i)))
6278c2ecf20Sopenharmony_ci			tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
6288c2ecf20Sopenharmony_ci		else
6298c2ecf20Sopenharmony_ci			tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	return 0;
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci/*
6368c2ecf20Sopenharmony_ci * We accessed the video status signal in the Techwell chip through
6378c2ecf20Sopenharmony_ci * iic/i2c because the video status reported by register REG_VI_STATUS1
6388c2ecf20Sopenharmony_ci * (address 0x012C) of the SOLO6010 chip doesn't give the correct video
6398c2ecf20Sopenharmony_ci * status signal values.
6408c2ecf20Sopenharmony_ci */
6418c2ecf20Sopenharmony_ciint tw28_get_video_status(struct solo_dev *solo_dev, u8 ch)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	u8 val, chip_num;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	/* Get the right chip and on-chip channel */
6468c2ecf20Sopenharmony_ci	chip_num = ch / 4;
6478c2ecf20Sopenharmony_ci	ch %= 4;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR,
6508c2ecf20Sopenharmony_ci			  TW_AV_STAT_ADDR) & 0x0f;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	return val & (1 << ch) ? 1 : 0;
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci#if 0
6568c2ecf20Sopenharmony_ci/* Status of audio from up to 4 techwell chips are combined into 1 variable.
6578c2ecf20Sopenharmony_ci * See techwell datasheet for details. */
6588c2ecf20Sopenharmony_ciu16 tw28_get_audio_status(struct solo_dev *solo_dev)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	u8 val;
6618c2ecf20Sopenharmony_ci	u16 status = 0;
6628c2ecf20Sopenharmony_ci	int i;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	for (i = 0; i < solo_dev->tw28_cnt; i++) {
6658c2ecf20Sopenharmony_ci		val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR,
6668c2ecf20Sopenharmony_ci				   TW_AV_STAT_ADDR) & 0xf0) >> 4;
6678c2ecf20Sopenharmony_ci		status |= val << (i * 4);
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	return status;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci#endif
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cibool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	return is_tw286x(solo_dev, ch / 4);
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ciint tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
6808c2ecf20Sopenharmony_ci		      s32 val)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	char sval;
6838c2ecf20Sopenharmony_ci	u8 chip_num;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* Get the right chip and on-chip channel */
6868c2ecf20Sopenharmony_ci	chip_num = ch / 4;
6878c2ecf20Sopenharmony_ci	ch %= 4;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (val > 255 || val < 0)
6908c2ecf20Sopenharmony_ci		return -ERANGE;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	switch (ctrl) {
6938c2ecf20Sopenharmony_ci	case V4L2_CID_SHARPNESS:
6948c2ecf20Sopenharmony_ci		/* Only 286x has sharpness */
6958c2ecf20Sopenharmony_ci		if (is_tw286x(solo_dev, chip_num)) {
6968c2ecf20Sopenharmony_ci			u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
6978c2ecf20Sopenharmony_ci						 TW_CHIP_OFFSET_ADDR(chip_num),
6988c2ecf20Sopenharmony_ci						 TW286x_SHARPNESS(chip_num));
6998c2ecf20Sopenharmony_ci			v &= 0xf0;
7008c2ecf20Sopenharmony_ci			v |= val;
7018c2ecf20Sopenharmony_ci			solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
7028c2ecf20Sopenharmony_ci					   TW_CHIP_OFFSET_ADDR(chip_num),
7038c2ecf20Sopenharmony_ci					   TW286x_SHARPNESS(chip_num), v);
7048c2ecf20Sopenharmony_ci		} else {
7058c2ecf20Sopenharmony_ci			return -EINVAL;
7068c2ecf20Sopenharmony_ci		}
7078c2ecf20Sopenharmony_ci		break;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	case V4L2_CID_HUE:
7108c2ecf20Sopenharmony_ci		if (is_tw286x(solo_dev, chip_num))
7118c2ecf20Sopenharmony_ci			sval = val - 128;
7128c2ecf20Sopenharmony_ci		else
7138c2ecf20Sopenharmony_ci			sval = (char)val;
7148c2ecf20Sopenharmony_ci		tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
7158c2ecf20Sopenharmony_ci			     TW_HUE_ADDR(ch), sval);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		break;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
7208c2ecf20Sopenharmony_ci		/* 286x chips have a U and V component for saturation */
7218c2ecf20Sopenharmony_ci		if (is_tw286x(solo_dev, chip_num)) {
7228c2ecf20Sopenharmony_ci			solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
7238c2ecf20Sopenharmony_ci					   TW_CHIP_OFFSET_ADDR(chip_num),
7248c2ecf20Sopenharmony_ci					   TW286x_SATURATIONU_ADDR(ch), val);
7258c2ecf20Sopenharmony_ci		}
7268c2ecf20Sopenharmony_ci		tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
7278c2ecf20Sopenharmony_ci			     TW_SATURATION_ADDR(ch), val);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci		break;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
7328c2ecf20Sopenharmony_ci		tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
7338c2ecf20Sopenharmony_ci			     TW_CONTRAST_ADDR(ch), val);
7348c2ecf20Sopenharmony_ci		break;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
7378c2ecf20Sopenharmony_ci		if (is_tw286x(solo_dev, chip_num))
7388c2ecf20Sopenharmony_ci			sval = val - 128;
7398c2ecf20Sopenharmony_ci		else
7408c2ecf20Sopenharmony_ci			sval = (char)val;
7418c2ecf20Sopenharmony_ci		tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
7428c2ecf20Sopenharmony_ci			     TW_BRIGHTNESS_ADDR(ch), sval);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		break;
7458c2ecf20Sopenharmony_ci	default:
7468c2ecf20Sopenharmony_ci		return -EINVAL;
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	return 0;
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ciint tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
7538c2ecf20Sopenharmony_ci		      s32 *val)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	u8 rval, chip_num;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/* Get the right chip and on-chip channel */
7588c2ecf20Sopenharmony_ci	chip_num = ch / 4;
7598c2ecf20Sopenharmony_ci	ch %= 4;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	switch (ctrl) {
7628c2ecf20Sopenharmony_ci	case V4L2_CID_SHARPNESS:
7638c2ecf20Sopenharmony_ci		/* Only 286x has sharpness */
7648c2ecf20Sopenharmony_ci		if (is_tw286x(solo_dev, chip_num)) {
7658c2ecf20Sopenharmony_ci			rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
7668c2ecf20Sopenharmony_ci						 TW_CHIP_OFFSET_ADDR(chip_num),
7678c2ecf20Sopenharmony_ci						 TW286x_SHARPNESS(chip_num));
7688c2ecf20Sopenharmony_ci			*val = rval & 0x0f;
7698c2ecf20Sopenharmony_ci		} else
7708c2ecf20Sopenharmony_ci			*val = 0;
7718c2ecf20Sopenharmony_ci		break;
7728c2ecf20Sopenharmony_ci	case V4L2_CID_HUE:
7738c2ecf20Sopenharmony_ci		rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
7748c2ecf20Sopenharmony_ci				   TW_HUE_ADDR(ch));
7758c2ecf20Sopenharmony_ci		if (is_tw286x(solo_dev, chip_num))
7768c2ecf20Sopenharmony_ci			*val = (s32)((char)rval) + 128;
7778c2ecf20Sopenharmony_ci		else
7788c2ecf20Sopenharmony_ci			*val = rval;
7798c2ecf20Sopenharmony_ci		break;
7808c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
7818c2ecf20Sopenharmony_ci		*val = tw_readbyte(solo_dev, chip_num,
7828c2ecf20Sopenharmony_ci				   TW286x_SATURATIONU_ADDR(ch),
7838c2ecf20Sopenharmony_ci				   TW_SATURATION_ADDR(ch));
7848c2ecf20Sopenharmony_ci		break;
7858c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
7868c2ecf20Sopenharmony_ci		*val = tw_readbyte(solo_dev, chip_num,
7878c2ecf20Sopenharmony_ci				   TW286x_CONTRAST_ADDR(ch),
7888c2ecf20Sopenharmony_ci				   TW_CONTRAST_ADDR(ch));
7898c2ecf20Sopenharmony_ci		break;
7908c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
7918c2ecf20Sopenharmony_ci		rval = tw_readbyte(solo_dev, chip_num,
7928c2ecf20Sopenharmony_ci				   TW286x_BRIGHTNESS_ADDR(ch),
7938c2ecf20Sopenharmony_ci				   TW_BRIGHTNESS_ADDR(ch));
7948c2ecf20Sopenharmony_ci		if (is_tw286x(solo_dev, chip_num))
7958c2ecf20Sopenharmony_ci			*val = (s32)((char)rval) + 128;
7968c2ecf20Sopenharmony_ci		else
7978c2ecf20Sopenharmony_ci			*val = rval;
7988c2ecf20Sopenharmony_ci		break;
7998c2ecf20Sopenharmony_ci	default:
8008c2ecf20Sopenharmony_ci		return -EINVAL;
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	return 0;
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci#if 0
8078c2ecf20Sopenharmony_ci/*
8088c2ecf20Sopenharmony_ci * For audio output volume, the output channel is only 1. In this case we
8098c2ecf20Sopenharmony_ci * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
8108c2ecf20Sopenharmony_ci * is the base address of the techwell chip.
8118c2ecf20Sopenharmony_ci */
8128c2ecf20Sopenharmony_civoid tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	unsigned int val;
8158c2ecf20Sopenharmony_ci	unsigned int chip_num;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	chip_num = (solo_dev->nr_chans - 1) / 4;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
8208c2ecf20Sopenharmony_ci			  TW_AUDIO_OUTPUT_VOL_ADDR);
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	u_val = (val & 0x0f) | (u_val << 4);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
8258c2ecf20Sopenharmony_ci		     TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci#endif
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ciu8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	u8 val;
8328c2ecf20Sopenharmony_ci	u8 chip_num;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	/* Get the right chip and on-chip channel */
8358c2ecf20Sopenharmony_ci	chip_num = ch / 4;
8368c2ecf20Sopenharmony_ci	ch %= 4;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	val = tw_readbyte(solo_dev, chip_num,
8398c2ecf20Sopenharmony_ci			  TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
8408c2ecf20Sopenharmony_ci			  TW_AUDIO_INPUT_GAIN_ADDR(ch));
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	return (ch % 2) ? (val >> 4) : (val & 0x0f);
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_civoid tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	u8 old_val;
8488c2ecf20Sopenharmony_ci	u8 chip_num;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	/* Get the right chip and on-chip channel */
8518c2ecf20Sopenharmony_ci	chip_num = ch / 4;
8528c2ecf20Sopenharmony_ci	ch %= 4;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	old_val = tw_readbyte(solo_dev, chip_num,
8558c2ecf20Sopenharmony_ci			      TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
8568c2ecf20Sopenharmony_ci			      TW_AUDIO_INPUT_GAIN_ADDR(ch));
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
8598c2ecf20Sopenharmony_ci		((ch % 2) ? (val << 4) : val);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
8628c2ecf20Sopenharmony_ci		     TW_AUDIO_INPUT_GAIN_ADDR(ch), val);
8638c2ecf20Sopenharmony_ci}
864