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