cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tw9906.c (5227B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2005-2006 Micronas USA Inc.
      4 */
      5
      6#include <linux/module.h>
      7#include <linux/init.h>
      8#include <linux/i2c.h>
      9#include <linux/videodev2.h>
     10#include <linux/ioctl.h>
     11#include <linux/slab.h>
     12#include <media/v4l2-device.h>
     13#include <media/v4l2-ctrls.h>
     14
     15MODULE_DESCRIPTION("TW9906 I2C subdev driver");
     16MODULE_LICENSE("GPL v2");
     17
     18struct tw9906 {
     19	struct v4l2_subdev sd;
     20	struct v4l2_ctrl_handler hdl;
     21	v4l2_std_id norm;
     22};
     23
     24static inline struct tw9906 *to_state(struct v4l2_subdev *sd)
     25{
     26	return container_of(sd, struct tw9906, sd);
     27}
     28
     29static const u8 initial_registers[] = {
     30	0x02, 0x40, /* input 0, composite */
     31	0x03, 0xa2, /* correct digital format */
     32	0x05, 0x81, /* or 0x01 for PAL */
     33	0x07, 0x02, /* window */
     34	0x08, 0x14, /* window */
     35	0x09, 0xf0, /* window */
     36	0x0a, 0x10, /* window */
     37	0x0b, 0xd0, /* window */
     38	0x0d, 0x00, /* scaling */
     39	0x0e, 0x11, /* scaling */
     40	0x0f, 0x00, /* scaling */
     41	0x10, 0x00, /* brightness */
     42	0x11, 0x60, /* contrast */
     43	0x12, 0x11, /* sharpness */
     44	0x13, 0x7e, /* U gain */
     45	0x14, 0x7e, /* V gain */
     46	0x15, 0x00, /* hue */
     47	0x19, 0x57, /* vbi */
     48	0x1a, 0x0f,
     49	0x1b, 0x40,
     50	0x29, 0x03,
     51	0x55, 0x00,
     52	0x6b, 0x26,
     53	0x6c, 0x36,
     54	0x6d, 0xf0,
     55	0x6e, 0x41,
     56	0x6f, 0x13,
     57	0xad, 0x70,
     58	0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
     59};
     60
     61static int write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
     62{
     63	struct i2c_client *client = v4l2_get_subdevdata(sd);
     64
     65	return i2c_smbus_write_byte_data(client, reg, value);
     66}
     67
     68static int write_regs(struct v4l2_subdev *sd, const u8 *regs)
     69{
     70	int i;
     71
     72	for (i = 0; regs[i] != 0x00; i += 2)
     73		if (write_reg(sd, regs[i], regs[i + 1]) < 0)
     74			return -1;
     75	return 0;
     76}
     77
     78static int tw9906_s_video_routing(struct v4l2_subdev *sd, u32 input,
     79				      u32 output, u32 config)
     80{
     81	write_reg(sd, 0x02, 0x40 | (input << 1));
     82	return 0;
     83}
     84
     85static int tw9906_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
     86{
     87	struct tw9906 *dec = to_state(sd);
     88	bool is_60hz = norm & V4L2_STD_525_60;
     89	static const u8 config_60hz[] = {
     90		0x05, 0x81,
     91		0x07, 0x02,
     92		0x08, 0x14,
     93		0x09, 0xf0,
     94		0,    0,
     95	};
     96	static const u8 config_50hz[] = {
     97		0x05, 0x01,
     98		0x07, 0x12,
     99		0x08, 0x18,
    100		0x09, 0x20,
    101		0,    0,
    102	};
    103
    104	write_regs(sd, is_60hz ? config_60hz : config_50hz);
    105	dec->norm = norm;
    106	return 0;
    107}
    108
    109static int tw9906_s_ctrl(struct v4l2_ctrl *ctrl)
    110{
    111	struct tw9906 *dec = container_of(ctrl->handler, struct tw9906, hdl);
    112	struct v4l2_subdev *sd = &dec->sd;
    113
    114	switch (ctrl->id) {
    115	case V4L2_CID_BRIGHTNESS:
    116		write_reg(sd, 0x10, ctrl->val);
    117		break;
    118	case V4L2_CID_CONTRAST:
    119		write_reg(sd, 0x11, ctrl->val);
    120		break;
    121	case V4L2_CID_HUE:
    122		write_reg(sd, 0x15, ctrl->val);
    123		break;
    124	default:
    125		return -EINVAL;
    126	}
    127	return 0;
    128}
    129
    130static int tw9906_log_status(struct v4l2_subdev *sd)
    131{
    132	struct tw9906 *dec = to_state(sd);
    133	bool is_60hz = dec->norm & V4L2_STD_525_60;
    134
    135	v4l2_info(sd, "Standard: %d Hz\n", is_60hz ? 60 : 50);
    136	v4l2_ctrl_subdev_log_status(sd);
    137	return 0;
    138}
    139
    140/* --------------------------------------------------------------------------*/
    141
    142static const struct v4l2_ctrl_ops tw9906_ctrl_ops = {
    143	.s_ctrl = tw9906_s_ctrl,
    144};
    145
    146static const struct v4l2_subdev_core_ops tw9906_core_ops = {
    147	.log_status = tw9906_log_status,
    148};
    149
    150static const struct v4l2_subdev_video_ops tw9906_video_ops = {
    151	.s_std = tw9906_s_std,
    152	.s_routing = tw9906_s_video_routing,
    153};
    154
    155static const struct v4l2_subdev_ops tw9906_ops = {
    156	.core = &tw9906_core_ops,
    157	.video = &tw9906_video_ops,
    158};
    159
    160static int tw9906_probe(struct i2c_client *client,
    161			     const struct i2c_device_id *id)
    162{
    163	struct tw9906 *dec;
    164	struct v4l2_subdev *sd;
    165	struct v4l2_ctrl_handler *hdl;
    166
    167	/* Check if the adapter supports the needed features */
    168	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
    169		return -EIO;
    170
    171	v4l_info(client, "chip found @ 0x%02x (%s)\n",
    172			client->addr << 1, client->adapter->name);
    173
    174	dec = devm_kzalloc(&client->dev, sizeof(*dec), GFP_KERNEL);
    175	if (dec == NULL)
    176		return -ENOMEM;
    177	sd = &dec->sd;
    178	v4l2_i2c_subdev_init(sd, client, &tw9906_ops);
    179	hdl = &dec->hdl;
    180	v4l2_ctrl_handler_init(hdl, 4);
    181	v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops,
    182		V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
    183	v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops,
    184		V4L2_CID_CONTRAST, 0, 255, 1, 0x60);
    185	v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops,
    186		V4L2_CID_HUE, -128, 127, 1, 0);
    187	sd->ctrl_handler = hdl;
    188	if (hdl->error) {
    189		int err = hdl->error;
    190
    191		v4l2_ctrl_handler_free(hdl);
    192		return err;
    193	}
    194
    195	/* Initialize tw9906 */
    196	dec->norm = V4L2_STD_NTSC;
    197
    198	if (write_regs(sd, initial_registers) < 0) {
    199		v4l2_err(client, "error initializing TW9906\n");
    200		return -EINVAL;
    201	}
    202
    203	return 0;
    204}
    205
    206static int tw9906_remove(struct i2c_client *client)
    207{
    208	struct v4l2_subdev *sd = i2c_get_clientdata(client);
    209
    210	v4l2_device_unregister_subdev(sd);
    211	v4l2_ctrl_handler_free(&to_state(sd)->hdl);
    212	return 0;
    213}
    214
    215/* ----------------------------------------------------------------------- */
    216
    217static const struct i2c_device_id tw9906_id[] = {
    218	{ "tw9906", 0 },
    219	{ }
    220};
    221MODULE_DEVICE_TABLE(i2c, tw9906_id);
    222
    223static struct i2c_driver tw9906_driver = {
    224	.driver = {
    225		.name	= "tw9906",
    226	},
    227	.probe = tw9906_probe,
    228	.remove = tw9906_remove,
    229	.id_table = tw9906_id,
    230};
    231module_i2c_driver(tw9906_driver);