]>
Commit | Line | Data |
---|---|---|
ac247433 HV |
1 | /* |
2 | * vp27smpx - driver version 0.0.1 | |
3 | * | |
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | |
5 | * | |
da80be21 KK |
6 | * Based on a tvaudio patch from Takahiro Adachi <tadachi@tadachi-net.com> |
7 | * and Kazuhiko Kawakami <kazz-0@mail.goo.ne.jp> | |
ac247433 HV |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
ac247433 HV |
18 | */ |
19 | ||
20 | #include <linux/module.h> | |
21 | #include <linux/types.h> | |
5a0e3ad6 | 22 | #include <linux/slab.h> |
ac247433 | 23 | #include <linux/ioctl.h> |
7c0f6ba6 | 24 | #include <linux/uaccess.h> |
ac247433 | 25 | #include <linux/i2c.h> |
33b687cf | 26 | #include <linux/videodev2.h> |
d9460f06 | 27 | #include <media/v4l2-device.h> |
ac247433 HV |
28 | |
29 | MODULE_DESCRIPTION("vp27smpx driver"); | |
30 | MODULE_AUTHOR("Hans Verkuil"); | |
31 | MODULE_LICENSE("GPL"); | |
32 | ||
ac247433 HV |
33 | |
34 | /* ----------------------------------------------------------------------- */ | |
35 | ||
36 | struct vp27smpx_state { | |
d9460f06 | 37 | struct v4l2_subdev sd; |
ac247433 HV |
38 | int radio; |
39 | u32 audmode; | |
40 | }; | |
41 | ||
d9460f06 | 42 | static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) |
ac247433 | 43 | { |
d9460f06 HV |
44 | return container_of(sd, struct vp27smpx_state, sd); |
45 | } | |
46 | ||
47 | static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) | |
48 | { | |
49 | struct vp27smpx_state *state = to_state(sd); | |
50 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
ac247433 HV |
51 | u8 data[3] = { 0x00, 0x00, 0x04 }; |
52 | ||
53 | switch (audmode) { | |
35df38c0 HV |
54 | case V4L2_TUNER_MODE_MONO: |
55 | case V4L2_TUNER_MODE_LANG1: | |
56 | break; | |
57 | case V4L2_TUNER_MODE_STEREO: | |
58 | case V4L2_TUNER_MODE_LANG1_LANG2: | |
59 | data[1] = 0x01; | |
60 | break; | |
61 | case V4L2_TUNER_MODE_LANG2: | |
62 | data[1] = 0x02; | |
63 | break; | |
ac247433 HV |
64 | } |
65 | ||
35df38c0 | 66 | if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) |
d9460f06 | 67 | v4l2_err(sd, "I/O error setting audmode\n"); |
35df38c0 | 68 | else |
ac247433 | 69 | state->audmode = audmode; |
ac247433 HV |
70 | } |
71 | ||
d9460f06 | 72 | static int vp27smpx_s_radio(struct v4l2_subdev *sd) |
ac247433 | 73 | { |
d9460f06 | 74 | struct vp27smpx_state *state = to_state(sd); |
ac247433 | 75 | |
d9460f06 HV |
76 | state->radio = 1; |
77 | return 0; | |
78 | } | |
ac247433 | 79 | |
d9460f06 HV |
80 | static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) |
81 | { | |
82 | struct vp27smpx_state *state = to_state(sd); | |
ac247433 | 83 | |
d9460f06 HV |
84 | state->radio = 0; |
85 | return 0; | |
86 | } | |
ac247433 | 87 | |
2f73c7c5 | 88 | static int vp27smpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) |
d9460f06 HV |
89 | { |
90 | struct vp27smpx_state *state = to_state(sd); | |
ac247433 | 91 | |
d9460f06 HV |
92 | if (!state->radio) |
93 | vp27smpx_set_audmode(sd, vt->audmode); | |
94 | return 0; | |
95 | } | |
ac247433 | 96 | |
d9460f06 HV |
97 | static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) |
98 | { | |
99 | struct vp27smpx_state *state = to_state(sd); | |
100 | ||
101 | if (state->radio) | |
102 | return 0; | |
103 | vt->audmode = state->audmode; | |
104 | vt->capability = V4L2_TUNER_CAP_STEREO | | |
105 | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; | |
106 | vt->rxsubchans = V4L2_TUNER_SUB_MONO; | |
107 | return 0; | |
108 | } | |
ac247433 | 109 | |
d9460f06 HV |
110 | static int vp27smpx_log_status(struct v4l2_subdev *sd) |
111 | { | |
112 | struct vp27smpx_state *state = to_state(sd); | |
113 | ||
114 | v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, | |
115 | state->radio ? " (Radio)" : ""); | |
ac247433 HV |
116 | return 0; |
117 | } | |
118 | ||
d9460f06 HV |
119 | /* ----------------------------------------------------------------------- */ |
120 | ||
121 | static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { | |
122 | .log_status = vp27smpx_log_status, | |
d9460f06 HV |
123 | }; |
124 | ||
125 | static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { | |
126 | .s_radio = vp27smpx_s_radio, | |
d9460f06 HV |
127 | .s_tuner = vp27smpx_s_tuner, |
128 | .g_tuner = vp27smpx_g_tuner, | |
129 | }; | |
130 | ||
8774bed9 LP |
131 | static const struct v4l2_subdev_video_ops vp27smpx_video_ops = { |
132 | .s_std = vp27smpx_s_std, | |
133 | }; | |
134 | ||
d9460f06 HV |
135 | static const struct v4l2_subdev_ops vp27smpx_ops = { |
136 | .core = &vp27smpx_core_ops, | |
137 | .tuner = &vp27smpx_tuner_ops, | |
8774bed9 | 138 | .video = &vp27smpx_video_ops, |
d9460f06 HV |
139 | }; |
140 | ||
ac247433 HV |
141 | /* ----------------------------------------------------------------------- */ |
142 | ||
143 | /* i2c implementation */ | |
144 | ||
145 | /* | |
146 | * Generic i2c probe | |
147 | * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' | |
148 | */ | |
149 | ||
d2653e92 JD |
150 | static int vp27smpx_probe(struct i2c_client *client, |
151 | const struct i2c_device_id *id) | |
ac247433 | 152 | { |
ac247433 | 153 | struct vp27smpx_state *state; |
d9460f06 | 154 | struct v4l2_subdev *sd; |
ac247433 HV |
155 | |
156 | /* Check if the adapter supports the needed features */ | |
45eea276 | 157 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
188f3457 | 158 | return -EIO; |
ac247433 | 159 | |
35df38c0 HV |
160 | v4l_info(client, "chip found @ 0x%x (%s)\n", |
161 | client->addr << 1, client->adapter->name); | |
ac247433 | 162 | |
c02b211d | 163 | state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); |
35df38c0 | 164 | if (state == NULL) |
ac247433 | 165 | return -ENOMEM; |
d9460f06 HV |
166 | sd = &state->sd; |
167 | v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); | |
ac247433 | 168 | state->audmode = V4L2_TUNER_MODE_STEREO; |
ac247433 HV |
169 | |
170 | /* initialize vp27smpx */ | |
d9460f06 | 171 | vp27smpx_set_audmode(sd, state->audmode); |
ac247433 HV |
172 | return 0; |
173 | } | |
174 | ||
45eea276 | 175 | static int vp27smpx_remove(struct i2c_client *client) |
ac247433 | 176 | { |
d9460f06 HV |
177 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
178 | ||
179 | v4l2_device_unregister_subdev(sd); | |
ac247433 HV |
180 | return 0; |
181 | } | |
182 | ||
183 | /* ----------------------------------------------------------------------- */ | |
184 | ||
af294867 JD |
185 | static const struct i2c_device_id vp27smpx_id[] = { |
186 | { "vp27smpx", 0 }, | |
187 | { } | |
188 | }; | |
189 | MODULE_DEVICE_TABLE(i2c, vp27smpx_id); | |
190 | ||
1f352667 HV |
191 | static struct i2c_driver vp27smpx_driver = { |
192 | .driver = { | |
1f352667 HV |
193 | .name = "vp27smpx", |
194 | }, | |
195 | .probe = vp27smpx_probe, | |
196 | .remove = vp27smpx_remove, | |
197 | .id_table = vp27smpx_id, | |
ac247433 | 198 | }; |
1f352667 | 199 | |
c6e8d86f | 200 | module_i2c_driver(vp27smpx_driver); |