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