diff options
Diffstat (limited to 'drivers/media/video/m52790.c')
-rw-r--r-- | drivers/media/video/m52790.c | 176 |
1 files changed, 111 insertions, 65 deletions
diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index 89a781c6929d..07be14a9fe7b 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c | |||
@@ -28,7 +28,7 @@ | |||
28 | #include <linux/i2c-id.h> | 28 | #include <linux/i2c-id.h> |
29 | #include <linux/videodev2.h> | 29 | #include <linux/videodev2.h> |
30 | #include <media/m52790.h> | 30 | #include <media/m52790.h> |
31 | #include <media/v4l2-common.h> | 31 | #include <media/v4l2-device.h> |
32 | #include <media/v4l2-chip-ident.h> | 32 | #include <media/v4l2-chip-ident.h> |
33 | #include <media/v4l2-i2c-drv.h> | 33 | #include <media/v4l2-i2c-drv.h> |
34 | 34 | ||
@@ -38,89 +38,130 @@ MODULE_LICENSE("GPL"); | |||
38 | 38 | ||
39 | 39 | ||
40 | struct m52790_state { | 40 | struct m52790_state { |
41 | struct v4l2_subdev sd; | ||
41 | u16 input; | 42 | u16 input; |
42 | u16 output; | 43 | u16 output; |
43 | }; | 44 | }; |
44 | 45 | ||
46 | static inline struct m52790_state *to_state(struct v4l2_subdev *sd) | ||
47 | { | ||
48 | return container_of(sd, struct m52790_state, sd); | ||
49 | } | ||
50 | |||
45 | /* ----------------------------------------------------------------------- */ | 51 | /* ----------------------------------------------------------------------- */ |
46 | 52 | ||
47 | static int m52790_write(struct i2c_client *client) | 53 | static int m52790_write(struct v4l2_subdev *sd) |
48 | { | 54 | { |
49 | struct m52790_state *state = i2c_get_clientdata(client); | 55 | struct m52790_state *state = to_state(sd); |
56 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
57 | |||
50 | u8 sw1 = (state->input | state->output) & 0xff; | 58 | u8 sw1 = (state->input | state->output) & 0xff; |
51 | u8 sw2 = (state->input | state->output) >> 8; | 59 | u8 sw2 = (state->input | state->output) >> 8; |
52 | 60 | ||
53 | return i2c_smbus_write_byte_data(client, sw1, sw2); | 61 | return i2c_smbus_write_byte_data(client, sw1, sw2); |
54 | } | 62 | } |
55 | 63 | ||
56 | static int m52790_command(struct i2c_client *client, unsigned int cmd, | 64 | /* Note: audio and video are linked and cannot be switched separately. |
57 | void *arg) | 65 | So audio and video routing commands are identical for this chip. |
66 | In theory the video amplifier and audio modes could be handled | ||
67 | separately for the output, but that seems to be overkill right now. | ||
68 | The same holds for implementing an audio mute control, this is now | ||
69 | part of the audio output routing. The normal case is that another | ||
70 | chip takes care of the actual muting so making it part of the | ||
71 | output routing seems to be the right thing to do for now. */ | ||
72 | static int m52790_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) | ||
58 | { | 73 | { |
59 | struct m52790_state *state = i2c_get_clientdata(client); | 74 | struct m52790_state *state = to_state(sd); |
60 | struct v4l2_routing *route = arg; | 75 | |
61 | 76 | state->input = route->input; | |
62 | /* Note: audio and video are linked and cannot be switched separately. | 77 | state->output = route->output; |
63 | So audio and video routing commands are identical for this chip. | 78 | m52790_write(sd); |
64 | In theory the video amplifier and audio modes could be handled | 79 | return 0; |
65 | separately for the output, but that seems to be overkill right now. | 80 | } |
66 | The same holds for implementing an audio mute control, this is now | ||
67 | part of the audio output routing. The normal case is that another | ||
68 | chip takes care of the actual muting so making it part of the | ||
69 | output routing seems to be the right thing to do for now. */ | ||
70 | switch (cmd) { | ||
71 | case VIDIOC_INT_G_AUDIO_ROUTING: | ||
72 | case VIDIOC_INT_G_VIDEO_ROUTING: | ||
73 | route->input = state->input; | ||
74 | route->output = state->output; | ||
75 | break; | ||
76 | |||
77 | case VIDIOC_INT_S_AUDIO_ROUTING: | ||
78 | case VIDIOC_INT_S_VIDEO_ROUTING: | ||
79 | state->input = route->input; | ||
80 | state->output = route->output; | ||
81 | m52790_write(client); | ||
82 | break; | ||
83 | 81 | ||
84 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 82 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
85 | case VIDIOC_DBG_G_REGISTER: | 83 | static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) |
86 | case VIDIOC_DBG_S_REGISTER: | 84 | { |
87 | { | 85 | struct m52790_state *state = to_state(sd); |
88 | struct v4l2_register *reg = arg; | 86 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
89 | |||
90 | if (!v4l2_chip_match_i2c_client(client, | ||
91 | reg->match_type, reg->match_chip)) | ||
92 | return -EINVAL; | ||
93 | if (!capable(CAP_SYS_ADMIN)) | ||
94 | return -EPERM; | ||
95 | if (reg->reg != 0) | ||
96 | return -EINVAL; | ||
97 | if (cmd == VIDIOC_DBG_G_REGISTER) | ||
98 | reg->val = state->input | state->output; | ||
99 | else { | ||
100 | state->input = reg->val & 0x0303; | ||
101 | state->output = reg->val & ~0x0303; | ||
102 | m52790_write(client); | ||
103 | } | ||
104 | break; | ||
105 | } | ||
106 | #endif | ||
107 | 87 | ||
108 | case VIDIOC_G_CHIP_IDENT: | 88 | if (!v4l2_chip_match_i2c_client(client, |
109 | return v4l2_chip_ident_i2c_client(client, arg, | 89 | reg->match_type, reg->match_chip)) |
110 | V4L2_IDENT_M52790, 0); | 90 | return -EINVAL; |
91 | if (!capable(CAP_SYS_ADMIN)) | ||
92 | return -EPERM; | ||
93 | if (reg->reg != 0) | ||
94 | return -EINVAL; | ||
95 | reg->val = state->input | state->output; | ||
96 | return 0; | ||
97 | } | ||
111 | 98 | ||
112 | case VIDIOC_LOG_STATUS: | 99 | static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) |
113 | v4l_info(client, "Switch 1: %02x\n", | 100 | { |
114 | (state->input | state->output) & 0xff); | 101 | struct m52790_state *state = to_state(sd); |
115 | v4l_info(client, "Switch 2: %02x\n", | 102 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
116 | (state->input | state->output) >> 8); | ||
117 | break; | ||
118 | 103 | ||
119 | default: | 104 | if (!v4l2_chip_match_i2c_client(client, |
105 | reg->match_type, reg->match_chip)) | ||
120 | return -EINVAL; | 106 | return -EINVAL; |
121 | } | 107 | if (!capable(CAP_SYS_ADMIN)) |
108 | return -EPERM; | ||
109 | if (reg->reg != 0) | ||
110 | return -EINVAL; | ||
111 | state->input = reg->val & 0x0303; | ||
112 | state->output = reg->val & ~0x0303; | ||
113 | m52790_write(sd); | ||
122 | return 0; | 114 | return 0; |
123 | } | 115 | } |
116 | #endif | ||
117 | |||
118 | static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) | ||
119 | { | ||
120 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
121 | |||
122 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0); | ||
123 | } | ||
124 | |||
125 | static int m52790_log_status(struct v4l2_subdev *sd) | ||
126 | { | ||
127 | struct m52790_state *state = to_state(sd); | ||
128 | |||
129 | v4l2_info(sd, "Switch 1: %02x\n", | ||
130 | (state->input | state->output) & 0xff); | ||
131 | v4l2_info(sd, "Switch 2: %02x\n", | ||
132 | (state->input | state->output) >> 8); | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static int m52790_command(struct i2c_client *client, unsigned cmd, void *arg) | ||
137 | { | ||
138 | return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); | ||
139 | } | ||
140 | |||
141 | /* ----------------------------------------------------------------------- */ | ||
142 | |||
143 | static const struct v4l2_subdev_core_ops m52790_core_ops = { | ||
144 | .log_status = m52790_log_status, | ||
145 | .g_chip_ident = m52790_g_chip_ident, | ||
146 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
147 | .g_register = m52790_g_register, | ||
148 | .s_register = m52790_s_register, | ||
149 | #endif | ||
150 | }; | ||
151 | |||
152 | static const struct v4l2_subdev_audio_ops m52790_audio_ops = { | ||
153 | .s_routing = m52790_s_routing, | ||
154 | }; | ||
155 | |||
156 | static const struct v4l2_subdev_video_ops m52790_video_ops = { | ||
157 | .s_routing = m52790_s_routing, | ||
158 | }; | ||
159 | |||
160 | static const struct v4l2_subdev_ops m52790_ops = { | ||
161 | .core = &m52790_core_ops, | ||
162 | .audio = &m52790_audio_ops, | ||
163 | .video = &m52790_video_ops, | ||
164 | }; | ||
124 | 165 | ||
125 | /* ----------------------------------------------------------------------- */ | 166 | /* ----------------------------------------------------------------------- */ |
126 | 167 | ||
@@ -130,6 +171,7 @@ static int m52790_probe(struct i2c_client *client, | |||
130 | const struct i2c_device_id *id) | 171 | const struct i2c_device_id *id) |
131 | { | 172 | { |
132 | struct m52790_state *state; | 173 | struct m52790_state *state; |
174 | struct v4l2_subdev *sd; | ||
133 | 175 | ||
134 | /* Check if the adapter supports the needed features */ | 176 | /* Check if the adapter supports the needed features */ |
135 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | 177 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
@@ -142,16 +184,20 @@ static int m52790_probe(struct i2c_client *client, | |||
142 | if (state == NULL) | 184 | if (state == NULL) |
143 | return -ENOMEM; | 185 | return -ENOMEM; |
144 | 186 | ||
187 | sd = &state->sd; | ||
188 | v4l2_i2c_subdev_init(sd, client, &m52790_ops); | ||
145 | state->input = M52790_IN_TUNER; | 189 | state->input = M52790_IN_TUNER; |
146 | state->output = M52790_OUT_STEREO; | 190 | state->output = M52790_OUT_STEREO; |
147 | i2c_set_clientdata(client, state); | 191 | m52790_write(sd); |
148 | m52790_write(client); | ||
149 | return 0; | 192 | return 0; |
150 | } | 193 | } |
151 | 194 | ||
152 | static int m52790_remove(struct i2c_client *client) | 195 | static int m52790_remove(struct i2c_client *client) |
153 | { | 196 | { |
154 | kfree(i2c_get_clientdata(client)); | 197 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
198 | |||
199 | v4l2_device_unregister_subdev(sd); | ||
200 | kfree(to_state(sd)); | ||
155 | return 0; | 201 | return 0; |
156 | } | 202 | } |
157 | 203 | ||