1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab * m52790 i2c ivtv driver.
4cb7a01acSMauro Carvalho Chehab * Copyright (C) 2007 Hans Verkuil
5cb7a01acSMauro Carvalho Chehab *
6cb7a01acSMauro Carvalho Chehab * A/V source switching Mitsubishi M52790SP/FP
7cb7a01acSMauro Carvalho Chehab */
8cb7a01acSMauro Carvalho Chehab
9cb7a01acSMauro Carvalho Chehab
10cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
11cb7a01acSMauro Carvalho Chehab #include <linux/types.h>
12cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
13cb7a01acSMauro Carvalho Chehab #include <linux/ioctl.h>
147c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
15cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
16cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
17b5dcee22SMauro Carvalho Chehab #include <media/i2c/m52790.h>
18cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
19cb7a01acSMauro Carvalho Chehab
20cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch");
21cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Hans Verkuil");
22cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
23cb7a01acSMauro Carvalho Chehab
24cb7a01acSMauro Carvalho Chehab
25cb7a01acSMauro Carvalho Chehab struct m52790_state {
26cb7a01acSMauro Carvalho Chehab struct v4l2_subdev sd;
27cb7a01acSMauro Carvalho Chehab u16 input;
28cb7a01acSMauro Carvalho Chehab u16 output;
29cb7a01acSMauro Carvalho Chehab };
30cb7a01acSMauro Carvalho Chehab
to_state(struct v4l2_subdev * sd)31cb7a01acSMauro Carvalho Chehab static inline struct m52790_state *to_state(struct v4l2_subdev *sd)
32cb7a01acSMauro Carvalho Chehab {
33cb7a01acSMauro Carvalho Chehab return container_of(sd, struct m52790_state, sd);
34cb7a01acSMauro Carvalho Chehab }
35cb7a01acSMauro Carvalho Chehab
36cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
37cb7a01acSMauro Carvalho Chehab
m52790_write(struct v4l2_subdev * sd)38cb7a01acSMauro Carvalho Chehab static int m52790_write(struct v4l2_subdev *sd)
39cb7a01acSMauro Carvalho Chehab {
40cb7a01acSMauro Carvalho Chehab struct m52790_state *state = to_state(sd);
41cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd);
42cb7a01acSMauro Carvalho Chehab
43cb7a01acSMauro Carvalho Chehab u8 sw1 = (state->input | state->output) & 0xff;
44cb7a01acSMauro Carvalho Chehab u8 sw2 = (state->input | state->output) >> 8;
45cb7a01acSMauro Carvalho Chehab
46cb7a01acSMauro Carvalho Chehab return i2c_smbus_write_byte_data(client, sw1, sw2);
47cb7a01acSMauro Carvalho Chehab }
48cb7a01acSMauro Carvalho Chehab
49cb7a01acSMauro Carvalho Chehab /* Note: audio and video are linked and cannot be switched separately.
50cb7a01acSMauro Carvalho Chehab So audio and video routing commands are identical for this chip.
51cb7a01acSMauro Carvalho Chehab In theory the video amplifier and audio modes could be handled
52cb7a01acSMauro Carvalho Chehab separately for the output, but that seems to be overkill right now.
53cb7a01acSMauro Carvalho Chehab The same holds for implementing an audio mute control, this is now
54cb7a01acSMauro Carvalho Chehab part of the audio output routing. The normal case is that another
55cb7a01acSMauro Carvalho Chehab chip takes care of the actual muting so making it part of the
56cb7a01acSMauro Carvalho Chehab output routing seems to be the right thing to do for now. */
m52790_s_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)57cb7a01acSMauro Carvalho Chehab static int m52790_s_routing(struct v4l2_subdev *sd,
58cb7a01acSMauro Carvalho Chehab u32 input, u32 output, u32 config)
59cb7a01acSMauro Carvalho Chehab {
60cb7a01acSMauro Carvalho Chehab struct m52790_state *state = to_state(sd);
61cb7a01acSMauro Carvalho Chehab
62cb7a01acSMauro Carvalho Chehab state->input = input;
63cb7a01acSMauro Carvalho Chehab state->output = output;
64cb7a01acSMauro Carvalho Chehab m52790_write(sd);
65cb7a01acSMauro Carvalho Chehab return 0;
66cb7a01acSMauro Carvalho Chehab }
67cb7a01acSMauro Carvalho Chehab
68cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
m52790_g_register(struct v4l2_subdev * sd,struct v4l2_dbg_register * reg)69cb7a01acSMauro Carvalho Chehab static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
70cb7a01acSMauro Carvalho Chehab {
71cb7a01acSMauro Carvalho Chehab struct m52790_state *state = to_state(sd);
72cb7a01acSMauro Carvalho Chehab
73cb7a01acSMauro Carvalho Chehab if (reg->reg != 0)
74cb7a01acSMauro Carvalho Chehab return -EINVAL;
75cb7a01acSMauro Carvalho Chehab reg->size = 1;
76cb7a01acSMauro Carvalho Chehab reg->val = state->input | state->output;
77cb7a01acSMauro Carvalho Chehab return 0;
78cb7a01acSMauro Carvalho Chehab }
79cb7a01acSMauro Carvalho Chehab
m52790_s_register(struct v4l2_subdev * sd,const struct v4l2_dbg_register * reg)80977ba3b1SHans Verkuil static int m52790_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
81cb7a01acSMauro Carvalho Chehab {
82cb7a01acSMauro Carvalho Chehab struct m52790_state *state = to_state(sd);
83cb7a01acSMauro Carvalho Chehab
84cb7a01acSMauro Carvalho Chehab if (reg->reg != 0)
85cb7a01acSMauro Carvalho Chehab return -EINVAL;
86cb7a01acSMauro Carvalho Chehab state->input = reg->val & 0x0303;
87cb7a01acSMauro Carvalho Chehab state->output = reg->val & ~0x0303;
88cb7a01acSMauro Carvalho Chehab m52790_write(sd);
89cb7a01acSMauro Carvalho Chehab return 0;
90cb7a01acSMauro Carvalho Chehab }
91cb7a01acSMauro Carvalho Chehab #endif
92cb7a01acSMauro Carvalho Chehab
m52790_log_status(struct v4l2_subdev * sd)93cb7a01acSMauro Carvalho Chehab static int m52790_log_status(struct v4l2_subdev *sd)
94cb7a01acSMauro Carvalho Chehab {
95cb7a01acSMauro Carvalho Chehab struct m52790_state *state = to_state(sd);
96cb7a01acSMauro Carvalho Chehab
97cb7a01acSMauro Carvalho Chehab v4l2_info(sd, "Switch 1: %02x\n",
98cb7a01acSMauro Carvalho Chehab (state->input | state->output) & 0xff);
99cb7a01acSMauro Carvalho Chehab v4l2_info(sd, "Switch 2: %02x\n",
100cb7a01acSMauro Carvalho Chehab (state->input | state->output) >> 8);
101cb7a01acSMauro Carvalho Chehab return 0;
102cb7a01acSMauro Carvalho Chehab }
103cb7a01acSMauro Carvalho Chehab
104cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
105cb7a01acSMauro Carvalho Chehab
106cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops m52790_core_ops = {
107cb7a01acSMauro Carvalho Chehab .log_status = m52790_log_status,
108cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
109cb7a01acSMauro Carvalho Chehab .g_register = m52790_g_register,
110cb7a01acSMauro Carvalho Chehab .s_register = m52790_s_register,
111cb7a01acSMauro Carvalho Chehab #endif
112cb7a01acSMauro Carvalho Chehab };
113cb7a01acSMauro Carvalho Chehab
114cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_audio_ops m52790_audio_ops = {
115cb7a01acSMauro Carvalho Chehab .s_routing = m52790_s_routing,
116cb7a01acSMauro Carvalho Chehab };
117cb7a01acSMauro Carvalho Chehab
118cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops m52790_video_ops = {
119cb7a01acSMauro Carvalho Chehab .s_routing = m52790_s_routing,
120cb7a01acSMauro Carvalho Chehab };
121cb7a01acSMauro Carvalho Chehab
122cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops m52790_ops = {
123cb7a01acSMauro Carvalho Chehab .core = &m52790_core_ops,
124cb7a01acSMauro Carvalho Chehab .audio = &m52790_audio_ops,
125cb7a01acSMauro Carvalho Chehab .video = &m52790_video_ops,
126cb7a01acSMauro Carvalho Chehab };
127cb7a01acSMauro Carvalho Chehab
128cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
129cb7a01acSMauro Carvalho Chehab
130cb7a01acSMauro Carvalho Chehab /* i2c implementation */
131cb7a01acSMauro Carvalho Chehab
m52790_probe(struct i2c_client * client)132270d9afaSUwe Kleine-König static int m52790_probe(struct i2c_client *client)
133cb7a01acSMauro Carvalho Chehab {
134cb7a01acSMauro Carvalho Chehab struct m52790_state *state;
135cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd;
136cb7a01acSMauro Carvalho Chehab
137cb7a01acSMauro Carvalho Chehab /* Check if the adapter supports the needed features */
138cb7a01acSMauro Carvalho Chehab if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
139cb7a01acSMauro Carvalho Chehab return -EIO;
140cb7a01acSMauro Carvalho Chehab
141cb7a01acSMauro Carvalho Chehab v4l_info(client, "chip found @ 0x%x (%s)\n",
142cb7a01acSMauro Carvalho Chehab client->addr << 1, client->adapter->name);
143cb7a01acSMauro Carvalho Chehab
144c02b211dSLaurent Pinchart state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
145cb7a01acSMauro Carvalho Chehab if (state == NULL)
146cb7a01acSMauro Carvalho Chehab return -ENOMEM;
147cb7a01acSMauro Carvalho Chehab
148cb7a01acSMauro Carvalho Chehab sd = &state->sd;
149cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(sd, client, &m52790_ops);
150cb7a01acSMauro Carvalho Chehab state->input = M52790_IN_TUNER;
151cb7a01acSMauro Carvalho Chehab state->output = M52790_OUT_STEREO;
152cb7a01acSMauro Carvalho Chehab m52790_write(sd);
153cb7a01acSMauro Carvalho Chehab return 0;
154cb7a01acSMauro Carvalho Chehab }
155cb7a01acSMauro Carvalho Chehab
m52790_remove(struct i2c_client * client)156ed5c2f5fSUwe Kleine-König static void m52790_remove(struct i2c_client *client)
157cb7a01acSMauro Carvalho Chehab {
158cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client);
159cb7a01acSMauro Carvalho Chehab
160cb7a01acSMauro Carvalho Chehab v4l2_device_unregister_subdev(sd);
161cb7a01acSMauro Carvalho Chehab }
162cb7a01acSMauro Carvalho Chehab
163cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
164cb7a01acSMauro Carvalho Chehab
165cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id m52790_id[] = {
166cb7a01acSMauro Carvalho Chehab { "m52790", 0 },
167cb7a01acSMauro Carvalho Chehab { }
168cb7a01acSMauro Carvalho Chehab };
169cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, m52790_id);
170cb7a01acSMauro Carvalho Chehab
171cb7a01acSMauro Carvalho Chehab static struct i2c_driver m52790_driver = {
172cb7a01acSMauro Carvalho Chehab .driver = {
173cb7a01acSMauro Carvalho Chehab .name = "m52790",
174cb7a01acSMauro Carvalho Chehab },
175*aaeb31c0SUwe Kleine-König .probe = m52790_probe,
176cb7a01acSMauro Carvalho Chehab .remove = m52790_remove,
177cb7a01acSMauro Carvalho Chehab .id_table = m52790_id,
178cb7a01acSMauro Carvalho Chehab };
179cb7a01acSMauro Carvalho Chehab
180cb7a01acSMauro Carvalho Chehab module_i2c_driver(m52790_driver);
181