xref: /openbmc/linux/drivers/media/i2c/m52790.c (revision aaeb31c0)
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