1cb7a01acSMauro Carvalho Chehab /* 2cb7a01acSMauro Carvalho Chehab bt866 - BT866 Digital Video Encoder (Rockwell Part) 3cb7a01acSMauro Carvalho Chehab 4cb7a01acSMauro Carvalho Chehab Copyright (C) 1999 Mike Bernson <mike@mlb.org> 5cb7a01acSMauro Carvalho Chehab Copyright (C) 1998 Dave Perks <dperks@ibm.net> 6cb7a01acSMauro Carvalho Chehab 7cb7a01acSMauro Carvalho Chehab Modifications for LML33/DC10plus unified driver 8cb7a01acSMauro Carvalho Chehab Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> 9cb7a01acSMauro Carvalho Chehab 10cb7a01acSMauro Carvalho Chehab This code was modify/ported from the saa7111 driver written 11cb7a01acSMauro Carvalho Chehab by Dave Perks. 12cb7a01acSMauro Carvalho Chehab 13cb7a01acSMauro Carvalho Chehab This code was adapted for the bt866 by Christer Weinigel and ported 14cb7a01acSMauro Carvalho Chehab to 2.6 by Martin Samuelsson. 15cb7a01acSMauro Carvalho Chehab 16cb7a01acSMauro Carvalho Chehab This program is free software; you can redistribute it and/or modify 17cb7a01acSMauro Carvalho Chehab it under the terms of the GNU General Public License as published by 18cb7a01acSMauro Carvalho Chehab the Free Software Foundation; either version 2 of the License, or 19cb7a01acSMauro Carvalho Chehab (at your option) any later version. 20cb7a01acSMauro Carvalho Chehab 21cb7a01acSMauro Carvalho Chehab This program is distributed in the hope that it will be useful, 22cb7a01acSMauro Carvalho Chehab but WITHOUT ANY WARRANTY; without even the implied warranty of 23cb7a01acSMauro Carvalho Chehab MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24cb7a01acSMauro Carvalho Chehab GNU General Public License for more details. 25cb7a01acSMauro Carvalho Chehab 26cb7a01acSMauro Carvalho Chehab You should have received a copy of the GNU General Public License 27cb7a01acSMauro Carvalho Chehab along with this program; if not, write to the Free Software 28cb7a01acSMauro Carvalho Chehab Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 29cb7a01acSMauro Carvalho Chehab */ 30cb7a01acSMauro Carvalho Chehab 31cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 32cb7a01acSMauro Carvalho Chehab #include <linux/types.h> 33cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 34cb7a01acSMauro Carvalho Chehab #include <linux/ioctl.h> 35cb7a01acSMauro Carvalho Chehab #include <asm/uaccess.h> 36cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 37cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h> 38cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 39cb7a01acSMauro Carvalho Chehab #include <media/v4l2-chip-ident.h> 40cb7a01acSMauro Carvalho Chehab 41cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Brooktree-866 video encoder driver"); 42cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Mike Bernson & Dave Perks"); 43cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 44cb7a01acSMauro Carvalho Chehab 45cb7a01acSMauro Carvalho Chehab static int debug; 46cb7a01acSMauro Carvalho Chehab module_param(debug, int, 0); 47cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)"); 48cb7a01acSMauro Carvalho Chehab 49cb7a01acSMauro Carvalho Chehab 50cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */ 51cb7a01acSMauro Carvalho Chehab 52cb7a01acSMauro Carvalho Chehab struct bt866 { 53cb7a01acSMauro Carvalho Chehab struct v4l2_subdev sd; 54cb7a01acSMauro Carvalho Chehab u8 reg[256]; 55cb7a01acSMauro Carvalho Chehab }; 56cb7a01acSMauro Carvalho Chehab 57cb7a01acSMauro Carvalho Chehab static inline struct bt866 *to_bt866(struct v4l2_subdev *sd) 58cb7a01acSMauro Carvalho Chehab { 59cb7a01acSMauro Carvalho Chehab return container_of(sd, struct bt866, sd); 60cb7a01acSMauro Carvalho Chehab } 61cb7a01acSMauro Carvalho Chehab 62cb7a01acSMauro Carvalho Chehab static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data) 63cb7a01acSMauro Carvalho Chehab { 64cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); 65cb7a01acSMauro Carvalho Chehab u8 buffer[2]; 66cb7a01acSMauro Carvalho Chehab int err; 67cb7a01acSMauro Carvalho Chehab 68cb7a01acSMauro Carvalho Chehab buffer[0] = subaddr; 69cb7a01acSMauro Carvalho Chehab buffer[1] = data; 70cb7a01acSMauro Carvalho Chehab 71cb7a01acSMauro Carvalho Chehab encoder->reg[subaddr] = data; 72cb7a01acSMauro Carvalho Chehab 73cb7a01acSMauro Carvalho Chehab v4l_dbg(1, debug, client, "write 0x%02x = 0x%02x\n", subaddr, data); 74cb7a01acSMauro Carvalho Chehab 75cb7a01acSMauro Carvalho Chehab for (err = 0; err < 3;) { 76cb7a01acSMauro Carvalho Chehab if (i2c_master_send(client, buffer, 2) == 2) 77cb7a01acSMauro Carvalho Chehab break; 78cb7a01acSMauro Carvalho Chehab err++; 79cb7a01acSMauro Carvalho Chehab v4l_warn(client, "error #%d writing to 0x%02x\n", 80cb7a01acSMauro Carvalho Chehab err, subaddr); 81cb7a01acSMauro Carvalho Chehab schedule_timeout_interruptible(msecs_to_jiffies(100)); 82cb7a01acSMauro Carvalho Chehab } 83cb7a01acSMauro Carvalho Chehab if (err == 3) { 84cb7a01acSMauro Carvalho Chehab v4l_warn(client, "giving up\n"); 85cb7a01acSMauro Carvalho Chehab return -1; 86cb7a01acSMauro Carvalho Chehab } 87cb7a01acSMauro Carvalho Chehab 88cb7a01acSMauro Carvalho Chehab return 0; 89cb7a01acSMauro Carvalho Chehab } 90cb7a01acSMauro Carvalho Chehab 91cb7a01acSMauro Carvalho Chehab static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) 92cb7a01acSMauro Carvalho Chehab { 93cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); 94cb7a01acSMauro Carvalho Chehab 95cb7a01acSMauro Carvalho Chehab /* Only PAL supported by this driver at the moment! */ 96cb7a01acSMauro Carvalho Chehab if (!(std & V4L2_STD_NTSC)) 97cb7a01acSMauro Carvalho Chehab return -EINVAL; 98cb7a01acSMauro Carvalho Chehab return 0; 99cb7a01acSMauro Carvalho Chehab } 100cb7a01acSMauro Carvalho Chehab 101cb7a01acSMauro Carvalho Chehab static int bt866_s_routing(struct v4l2_subdev *sd, 102cb7a01acSMauro Carvalho Chehab u32 input, u32 output, u32 config) 103cb7a01acSMauro Carvalho Chehab { 104cb7a01acSMauro Carvalho Chehab static const __u8 init[] = { 105cb7a01acSMauro Carvalho Chehab 0xc8, 0xcc, /* CRSCALE */ 106cb7a01acSMauro Carvalho Chehab 0xca, 0x91, /* CBSCALE */ 107cb7a01acSMauro Carvalho Chehab 0xcc, 0x24, /* YC16 | OSDNUM */ 108cb7a01acSMauro Carvalho Chehab 0xda, 0x00, /* */ 109cb7a01acSMauro Carvalho Chehab 0xdc, 0x24, /* SETMODE | PAL */ 110cb7a01acSMauro Carvalho Chehab 0xde, 0x02, /* EACTIVE */ 111cb7a01acSMauro Carvalho Chehab 112cb7a01acSMauro Carvalho Chehab /* overlay colors */ 113cb7a01acSMauro Carvalho Chehab 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */ 114cb7a01acSMauro Carvalho Chehab 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */ 115cb7a01acSMauro Carvalho Chehab 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */ 116cb7a01acSMauro Carvalho Chehab 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */ 117cb7a01acSMauro Carvalho Chehab 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */ 118cb7a01acSMauro Carvalho Chehab 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */ 119cb7a01acSMauro Carvalho Chehab 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */ 120cb7a01acSMauro Carvalho Chehab 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */ 121cb7a01acSMauro Carvalho Chehab 122cb7a01acSMauro Carvalho Chehab 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */ 123cb7a01acSMauro Carvalho Chehab 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */ 124cb7a01acSMauro Carvalho Chehab 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */ 125cb7a01acSMauro Carvalho Chehab 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */ 126cb7a01acSMauro Carvalho Chehab 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */ 127cb7a01acSMauro Carvalho Chehab 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */ 128cb7a01acSMauro Carvalho Chehab 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */ 129cb7a01acSMauro Carvalho Chehab 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */ 130cb7a01acSMauro Carvalho Chehab }; 131cb7a01acSMauro Carvalho Chehab struct bt866 *encoder = to_bt866(sd); 132cb7a01acSMauro Carvalho Chehab u8 val; 133cb7a01acSMauro Carvalho Chehab int i; 134cb7a01acSMauro Carvalho Chehab 135cb7a01acSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2) 136cb7a01acSMauro Carvalho Chehab bt866_write(encoder, init[i], init[i+1]); 137cb7a01acSMauro Carvalho Chehab 138cb7a01acSMauro Carvalho Chehab val = encoder->reg[0xdc]; 139cb7a01acSMauro Carvalho Chehab 140cb7a01acSMauro Carvalho Chehab if (input == 0) 141cb7a01acSMauro Carvalho Chehab val |= 0x40; /* CBSWAP */ 142cb7a01acSMauro Carvalho Chehab else 143cb7a01acSMauro Carvalho Chehab val &= ~0x40; /* !CBSWAP */ 144cb7a01acSMauro Carvalho Chehab 145cb7a01acSMauro Carvalho Chehab bt866_write(encoder, 0xdc, val); 146cb7a01acSMauro Carvalho Chehab 147cb7a01acSMauro Carvalho Chehab val = encoder->reg[0xcc]; 148cb7a01acSMauro Carvalho Chehab if (input == 2) 149cb7a01acSMauro Carvalho Chehab val |= 0x01; /* OSDBAR */ 150cb7a01acSMauro Carvalho Chehab else 151cb7a01acSMauro Carvalho Chehab val &= ~0x01; /* !OSDBAR */ 152cb7a01acSMauro Carvalho Chehab bt866_write(encoder, 0xcc, val); 153cb7a01acSMauro Carvalho Chehab 154cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "set input %d\n", input); 155cb7a01acSMauro Carvalho Chehab 156cb7a01acSMauro Carvalho Chehab switch (input) { 157cb7a01acSMauro Carvalho Chehab case 0: 158cb7a01acSMauro Carvalho Chehab case 1: 159cb7a01acSMauro Carvalho Chehab case 2: 160cb7a01acSMauro Carvalho Chehab break; 161cb7a01acSMauro Carvalho Chehab default: 162cb7a01acSMauro Carvalho Chehab return -EINVAL; 163cb7a01acSMauro Carvalho Chehab } 164cb7a01acSMauro Carvalho Chehab return 0; 165cb7a01acSMauro Carvalho Chehab } 166cb7a01acSMauro Carvalho Chehab 167cb7a01acSMauro Carvalho Chehab #if 0 168cb7a01acSMauro Carvalho Chehab /* Code to setup square pixels, might be of some use in the future, 169cb7a01acSMauro Carvalho Chehab but is currently unused. */ 170cb7a01acSMauro Carvalho Chehab val = encoder->reg[0xdc]; 171cb7a01acSMauro Carvalho Chehab if (*iarg) 172cb7a01acSMauro Carvalho Chehab val |= 1; /* SQUARE */ 173cb7a01acSMauro Carvalho Chehab else 174cb7a01acSMauro Carvalho Chehab val &= ~1; /* !SQUARE */ 175cb7a01acSMauro Carvalho Chehab bt866_write(client, 0xdc, val); 176cb7a01acSMauro Carvalho Chehab #endif 177cb7a01acSMauro Carvalho Chehab 178cb7a01acSMauro Carvalho Chehab static int bt866_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) 179cb7a01acSMauro Carvalho Chehab { 180cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 181cb7a01acSMauro Carvalho Chehab 182cb7a01acSMauro Carvalho Chehab return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT866, 0); 183cb7a01acSMauro Carvalho Chehab } 184cb7a01acSMauro Carvalho Chehab 185cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */ 186cb7a01acSMauro Carvalho Chehab 187cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops bt866_core_ops = { 188cb7a01acSMauro Carvalho Chehab .g_chip_ident = bt866_g_chip_ident, 189cb7a01acSMauro Carvalho Chehab }; 190cb7a01acSMauro Carvalho Chehab 191cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops bt866_video_ops = { 192cb7a01acSMauro Carvalho Chehab .s_std_output = bt866_s_std_output, 193cb7a01acSMauro Carvalho Chehab .s_routing = bt866_s_routing, 194cb7a01acSMauro Carvalho Chehab }; 195cb7a01acSMauro Carvalho Chehab 196cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops bt866_ops = { 197cb7a01acSMauro Carvalho Chehab .core = &bt866_core_ops, 198cb7a01acSMauro Carvalho Chehab .video = &bt866_video_ops, 199cb7a01acSMauro Carvalho Chehab }; 200cb7a01acSMauro Carvalho Chehab 201cb7a01acSMauro Carvalho Chehab static int bt866_probe(struct i2c_client *client, 202cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *id) 203cb7a01acSMauro Carvalho Chehab { 204cb7a01acSMauro Carvalho Chehab struct bt866 *encoder; 205cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd; 206cb7a01acSMauro Carvalho Chehab 207cb7a01acSMauro Carvalho Chehab v4l_info(client, "chip found @ 0x%x (%s)\n", 208cb7a01acSMauro Carvalho Chehab client->addr << 1, client->adapter->name); 209cb7a01acSMauro Carvalho Chehab 210*c02b211dSLaurent Pinchart encoder = devm_kzalloc(&client->dev, sizeof(*encoder), GFP_KERNEL); 211cb7a01acSMauro Carvalho Chehab if (encoder == NULL) 212cb7a01acSMauro Carvalho Chehab return -ENOMEM; 213cb7a01acSMauro Carvalho Chehab sd = &encoder->sd; 214cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(sd, client, &bt866_ops); 215cb7a01acSMauro Carvalho Chehab return 0; 216cb7a01acSMauro Carvalho Chehab } 217cb7a01acSMauro Carvalho Chehab 218cb7a01acSMauro Carvalho Chehab static int bt866_remove(struct i2c_client *client) 219cb7a01acSMauro Carvalho Chehab { 220cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client); 221cb7a01acSMauro Carvalho Chehab 222cb7a01acSMauro Carvalho Chehab v4l2_device_unregister_subdev(sd); 223cb7a01acSMauro Carvalho Chehab return 0; 224cb7a01acSMauro Carvalho Chehab } 225cb7a01acSMauro Carvalho Chehab 226cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id bt866_id[] = { 227cb7a01acSMauro Carvalho Chehab { "bt866", 0 }, 228cb7a01acSMauro Carvalho Chehab { } 229cb7a01acSMauro Carvalho Chehab }; 230cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, bt866_id); 231cb7a01acSMauro Carvalho Chehab 232cb7a01acSMauro Carvalho Chehab static struct i2c_driver bt866_driver = { 233cb7a01acSMauro Carvalho Chehab .driver = { 234cb7a01acSMauro Carvalho Chehab .owner = THIS_MODULE, 235cb7a01acSMauro Carvalho Chehab .name = "bt866", 236cb7a01acSMauro Carvalho Chehab }, 237cb7a01acSMauro Carvalho Chehab .probe = bt866_probe, 238cb7a01acSMauro Carvalho Chehab .remove = bt866_remove, 239cb7a01acSMauro Carvalho Chehab .id_table = bt866_id, 240cb7a01acSMauro Carvalho Chehab }; 241cb7a01acSMauro Carvalho Chehab 242cb7a01acSMauro Carvalho Chehab module_i2c_driver(bt866_driver); 243