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

ak881x.c (7734B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM)
      4 *
      5 * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
      6 */
      7
      8#include <linux/i2c.h>
      9#include <linux/init.h>
     10#include <linux/platform_device.h>
     11#include <linux/slab.h>
     12#include <linux/videodev2.h>
     13#include <linux/module.h>
     14
     15#include <media/i2c/ak881x.h>
     16#include <media/v4l2-common.h>
     17#include <media/v4l2-device.h>
     18
     19#define AK881X_INTERFACE_MODE	0
     20#define AK881X_VIDEO_PROCESS1	1
     21#define AK881X_VIDEO_PROCESS2	2
     22#define AK881X_VIDEO_PROCESS3	3
     23#define AK881X_DAC_MODE		5
     24#define AK881X_STATUS		0x24
     25#define AK881X_DEVICE_ID	0x25
     26#define AK881X_DEVICE_REVISION	0x26
     27
     28struct ak881x {
     29	struct v4l2_subdev subdev;
     30	struct ak881x_pdata *pdata;
     31	unsigned int lines;
     32	char revision;	/* DEVICE_REVISION content */
     33};
     34
     35static int reg_read(struct i2c_client *client, const u8 reg)
     36{
     37	return i2c_smbus_read_byte_data(client, reg);
     38}
     39
     40static int reg_write(struct i2c_client *client, const u8 reg,
     41		     const u8 data)
     42{
     43	return i2c_smbus_write_byte_data(client, reg, data);
     44}
     45
     46static int reg_set(struct i2c_client *client, const u8 reg,
     47		   const u8 data, u8 mask)
     48{
     49	int ret = reg_read(client, reg);
     50	if (ret < 0)
     51		return ret;
     52	return reg_write(client, reg, (ret & ~mask) | (data & mask));
     53}
     54
     55static struct ak881x *to_ak881x(const struct i2c_client *client)
     56{
     57	return container_of(i2c_get_clientdata(client), struct ak881x, subdev);
     58}
     59
     60#ifdef CONFIG_VIDEO_ADV_DEBUG
     61static int ak881x_g_register(struct v4l2_subdev *sd,
     62			     struct v4l2_dbg_register *reg)
     63{
     64	struct i2c_client *client = v4l2_get_subdevdata(sd);
     65
     66	if (reg->reg > 0x26)
     67		return -EINVAL;
     68
     69	reg->size = 1;
     70	reg->val = reg_read(client, reg->reg);
     71
     72	if (reg->val > 0xffff)
     73		return -EIO;
     74
     75	return 0;
     76}
     77
     78static int ak881x_s_register(struct v4l2_subdev *sd,
     79			     const struct v4l2_dbg_register *reg)
     80{
     81	struct i2c_client *client = v4l2_get_subdevdata(sd);
     82
     83	if (reg->reg > 0x26)
     84		return -EINVAL;
     85
     86	if (reg_write(client, reg->reg, reg->val) < 0)
     87		return -EIO;
     88
     89	return 0;
     90}
     91#endif
     92
     93static int ak881x_fill_fmt(struct v4l2_subdev *sd,
     94		struct v4l2_subdev_state *sd_state,
     95		struct v4l2_subdev_format *format)
     96{
     97	struct v4l2_mbus_framefmt *mf = &format->format;
     98	struct i2c_client *client = v4l2_get_subdevdata(sd);
     99	struct ak881x *ak881x = to_ak881x(client);
    100
    101	if (format->pad)
    102		return -EINVAL;
    103
    104	v4l_bound_align_image(&mf->width, 0, 720, 2,
    105			      &mf->height, 0, ak881x->lines, 1, 0);
    106	mf->field	= V4L2_FIELD_INTERLACED;
    107	mf->code	= MEDIA_BUS_FMT_YUYV8_2X8;
    108	mf->colorspace	= V4L2_COLORSPACE_SMPTE170M;
    109
    110	return 0;
    111}
    112
    113static int ak881x_enum_mbus_code(struct v4l2_subdev *sd,
    114		struct v4l2_subdev_state *sd_state,
    115		struct v4l2_subdev_mbus_code_enum *code)
    116{
    117	if (code->pad || code->index)
    118		return -EINVAL;
    119
    120	code->code = MEDIA_BUS_FMT_YUYV8_2X8;
    121	return 0;
    122}
    123
    124static int ak881x_get_selection(struct v4l2_subdev *sd,
    125				struct v4l2_subdev_state *sd_state,
    126				struct v4l2_subdev_selection *sel)
    127{
    128	struct i2c_client *client = v4l2_get_subdevdata(sd);
    129	struct ak881x *ak881x = to_ak881x(client);
    130
    131	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
    132		return -EINVAL;
    133
    134	switch (sel->target) {
    135	case V4L2_SEL_TGT_CROP_BOUNDS:
    136		sel->r.left = 0;
    137		sel->r.top = 0;
    138		sel->r.width = 720;
    139		sel->r.height = ak881x->lines;
    140		return 0;
    141	default:
    142		return -EINVAL;
    143	}
    144}
    145
    146static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
    147{
    148	struct i2c_client *client = v4l2_get_subdevdata(sd);
    149	struct ak881x *ak881x = to_ak881x(client);
    150	u8 vp1;
    151
    152	if (std == V4L2_STD_NTSC_443) {
    153		vp1 = 3;
    154		ak881x->lines = 480;
    155	} else if (std == V4L2_STD_PAL_M) {
    156		vp1 = 5;
    157		ak881x->lines = 480;
    158	} else if (std == V4L2_STD_PAL_60) {
    159		vp1 = 7;
    160		ak881x->lines = 480;
    161	} else if (std & V4L2_STD_NTSC) {
    162		vp1 = 0;
    163		ak881x->lines = 480;
    164	} else if (std & V4L2_STD_PAL) {
    165		vp1 = 0xf;
    166		ak881x->lines = 576;
    167	} else {
    168		/* No SECAM or PAL_N/Nc supported */
    169		return -EINVAL;
    170	}
    171
    172	reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf);
    173
    174	return 0;
    175}
    176
    177static int ak881x_s_stream(struct v4l2_subdev *sd, int enable)
    178{
    179	struct i2c_client *client = v4l2_get_subdevdata(sd);
    180	struct ak881x *ak881x = to_ak881x(client);
    181
    182	if (enable) {
    183		u8 dac;
    184		/* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */
    185		/* Default: composite output */
    186		if (ak881x->pdata->flags & AK881X_COMPONENT)
    187			dac = 3;
    188		else
    189			dac = 4;
    190		/* Turn on the DAC(s) */
    191		reg_write(client, AK881X_DAC_MODE, dac);
    192		dev_dbg(&client->dev, "chip status 0x%x\n",
    193			reg_read(client, AK881X_STATUS));
    194	} else {
    195		/* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */
    196		reg_write(client, AK881X_DAC_MODE, 0);
    197		dev_dbg(&client->dev, "chip status 0x%x\n",
    198			reg_read(client, AK881X_STATUS));
    199	}
    200
    201	return 0;
    202}
    203
    204static const struct v4l2_subdev_core_ops ak881x_subdev_core_ops = {
    205#ifdef CONFIG_VIDEO_ADV_DEBUG
    206	.g_register	= ak881x_g_register,
    207	.s_register	= ak881x_s_register,
    208#endif
    209};
    210
    211static const struct v4l2_subdev_video_ops ak881x_subdev_video_ops = {
    212	.s_std_output	= ak881x_s_std_output,
    213	.s_stream	= ak881x_s_stream,
    214};
    215
    216static const struct v4l2_subdev_pad_ops ak881x_subdev_pad_ops = {
    217	.enum_mbus_code = ak881x_enum_mbus_code,
    218	.get_selection	= ak881x_get_selection,
    219	.set_fmt	= ak881x_fill_fmt,
    220	.get_fmt	= ak881x_fill_fmt,
    221};
    222
    223static const struct v4l2_subdev_ops ak881x_subdev_ops = {
    224	.core	= &ak881x_subdev_core_ops,
    225	.video	= &ak881x_subdev_video_ops,
    226	.pad	= &ak881x_subdev_pad_ops,
    227};
    228
    229static int ak881x_probe(struct i2c_client *client,
    230			const struct i2c_device_id *did)
    231{
    232	struct i2c_adapter *adapter = client->adapter;
    233	struct ak881x *ak881x;
    234	u8 ifmode, data;
    235
    236	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
    237		dev_warn(&adapter->dev,
    238			 "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
    239		return -EIO;
    240	}
    241
    242	ak881x = devm_kzalloc(&client->dev, sizeof(*ak881x), GFP_KERNEL);
    243	if (!ak881x)
    244		return -ENOMEM;
    245
    246	v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops);
    247
    248	data = reg_read(client, AK881X_DEVICE_ID);
    249
    250	switch (data) {
    251	case 0x13:
    252	case 0x14:
    253		break;
    254	default:
    255		dev_err(&client->dev,
    256			"No ak881x chip detected, register read %x\n", data);
    257		return -ENODEV;
    258	}
    259
    260	ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION);
    261	ak881x->pdata = client->dev.platform_data;
    262
    263	if (ak881x->pdata) {
    264		if (ak881x->pdata->flags & AK881X_FIELD)
    265			ifmode = 4;
    266		else
    267			ifmode = 0;
    268
    269		switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) {
    270		case AK881X_IF_MODE_BT656:
    271			ifmode |= 1;
    272			break;
    273		case AK881X_IF_MODE_MASTER:
    274			ifmode |= 2;
    275			break;
    276		case AK881X_IF_MODE_SLAVE:
    277		default:
    278			break;
    279		}
    280
    281		dev_dbg(&client->dev, "IF mode %x\n", ifmode);
    282
    283		/*
    284		 * "Line Blanking No." seems to be the same as the number of
    285		 * "black" lines on, e.g., SuperH VOU, whose default value of 20
    286		 * "incidentally" matches ak881x' default
    287		 */
    288		reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3));
    289	}
    290
    291	/* Hardware default: NTSC-M */
    292	ak881x->lines = 480;
    293
    294	dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n",
    295		 data, ak881x->revision);
    296
    297	return 0;
    298}
    299
    300static int ak881x_remove(struct i2c_client *client)
    301{
    302	struct ak881x *ak881x = to_ak881x(client);
    303
    304	v4l2_device_unregister_subdev(&ak881x->subdev);
    305
    306	return 0;
    307}
    308
    309static const struct i2c_device_id ak881x_id[] = {
    310	{ "ak8813", 0 },
    311	{ "ak8814", 0 },
    312	{ }
    313};
    314MODULE_DEVICE_TABLE(i2c, ak881x_id);
    315
    316static struct i2c_driver ak881x_i2c_driver = {
    317	.driver = {
    318		.name = "ak881x",
    319	},
    320	.probe		= ak881x_probe,
    321	.remove		= ak881x_remove,
    322	.id_table	= ak881x_id,
    323};
    324
    325module_i2c_driver(ak881x_i2c_driver);
    326
    327MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814");
    328MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
    329MODULE_LICENSE("GPL v2");