diff options
author | Olof Johansson <olof@lixom.net> | 2012-09-16 21:31:37 -0400 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2012-09-16 21:31:37 -0400 |
commit | 2e6185f1fea6cf88e9ce25cde1d6291ddfb3d4f0 (patch) | |
tree | c45ae7bace055c258fba5c4c6c0340b1e3f17f05 /drivers/media/radio/radio-shark.c | |
parent | 7405a749ae14f846cc2892c36d1a9343b0264b7c (diff) | |
parent | fd301cc4e5ba839050be135a178031bcd0d363a5 (diff) |
Merge tag 'tegra-for-3.7-drivers-i2c' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra into next/drivers
From Stephen Warren:
ARM: tegra: i2c driver enhancements mostly related to clocking
This branch contains a number of fixes and cleanups to the Tegra I2C
driver related to clocks. These are based on the common clock conversion
in order to avoid duplicating the clock driver changes before and after
the conversion. Finally, a bug-fix related to I2C_M_NOSTART is included.
This branch is based on previous pull request tegra-for-3.7-common-clk.
* tag 'tegra-for-3.7-drivers-i2c' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra:
i2c: tegra: dynamically control fast clk
i2c: tegra: I2_M_NOSTART functionality not supported in Tegra20
ARM: tegra: clock: remove unused clock entry for i2c
ARM: tegra: clock: add connection name in i2c clock entry
i2c: tegra: pass proper name for getting clock
ARM: tegra: clock: add i2c fast clock entry in clock table
ARM: Tegra: Add smp_twd clock for Tegra20
ARM: tegra: cpu-tegra: explicitly manage re-parenting
ARM: tegra: fix overflow in tegra20_pll_clk_round_rate()
ARM: tegra: Fix data type for io address
ARM: tegra: remove tegra_timer from tegra_list_clks
ARM: tegra30: clocks: fix the wrong tegra_audio_sync_clk_ops name
ARM: tegra: clocks: separate tegra_clk_32k_ops from Tegra20 and Tegra30
ARM: tegra: Remove duplicate code
ARM: tegra: Port tegra to generic clock framework
ARM: tegra: Add clk_tegra structure and helper functions
ARM: tegra: Rename tegra20 clock file
ARM: tegra20: Separate out clk ops and clk data
ARM: tegra30: Separate out clk ops and clk data
ARM: tegra: fix U16 divider range check
...
+ sync to v3.6-rc4
Resolved remove/modify conflict in arch/arm/mach-sa1100/leds-hackkit.c
caused by the sync with v3.6-rc4.
Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'drivers/media/radio/radio-shark.c')
-rw-r--r-- | drivers/media/radio/radio-shark.c | 151 |
1 files changed, 78 insertions, 73 deletions
diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c index d0b6bb507634..72ded29728bb 100644 --- a/drivers/media/radio/radio-shark.c +++ b/drivers/media/radio/radio-shark.c | |||
@@ -35,6 +35,11 @@ | |||
35 | #include <media/v4l2-device.h> | 35 | #include <media/v4l2-device.h> |
36 | #include <sound/tea575x-tuner.h> | 36 | #include <sound/tea575x-tuner.h> |
37 | 37 | ||
38 | #if defined(CONFIG_LEDS_CLASS) || \ | ||
39 | (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK_MODULE)) | ||
40 | #define SHARK_USE_LEDS 1 | ||
41 | #endif | ||
42 | |||
38 | /* | 43 | /* |
39 | * Version Information | 44 | * Version Information |
40 | */ | 45 | */ |
@@ -56,44 +61,18 @@ MODULE_LICENSE("GPL"); | |||
56 | 61 | ||
57 | enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS }; | 62 | enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS }; |
58 | 63 | ||
59 | static void shark_led_set_blue(struct led_classdev *led_cdev, | ||
60 | enum led_brightness value); | ||
61 | static void shark_led_set_blue_pulse(struct led_classdev *led_cdev, | ||
62 | enum led_brightness value); | ||
63 | static void shark_led_set_red(struct led_classdev *led_cdev, | ||
64 | enum led_brightness value); | ||
65 | |||
66 | static const struct led_classdev shark_led_templates[NO_LEDS] = { | ||
67 | [BLUE_LED] = { | ||
68 | .name = "%s:blue:", | ||
69 | .brightness = LED_OFF, | ||
70 | .max_brightness = 127, | ||
71 | .brightness_set = shark_led_set_blue, | ||
72 | }, | ||
73 | [BLUE_PULSE_LED] = { | ||
74 | .name = "%s:blue-pulse:", | ||
75 | .brightness = LED_OFF, | ||
76 | .max_brightness = 255, | ||
77 | .brightness_set = shark_led_set_blue_pulse, | ||
78 | }, | ||
79 | [RED_LED] = { | ||
80 | .name = "%s:red:", | ||
81 | .brightness = LED_OFF, | ||
82 | .max_brightness = 1, | ||
83 | .brightness_set = shark_led_set_red, | ||
84 | }, | ||
85 | }; | ||
86 | |||
87 | struct shark_device { | 64 | struct shark_device { |
88 | struct usb_device *usbdev; | 65 | struct usb_device *usbdev; |
89 | struct v4l2_device v4l2_dev; | 66 | struct v4l2_device v4l2_dev; |
90 | struct snd_tea575x tea; | 67 | struct snd_tea575x tea; |
91 | 68 | ||
69 | #ifdef SHARK_USE_LEDS | ||
92 | struct work_struct led_work; | 70 | struct work_struct led_work; |
93 | struct led_classdev leds[NO_LEDS]; | 71 | struct led_classdev leds[NO_LEDS]; |
94 | char led_names[NO_LEDS][32]; | 72 | char led_names[NO_LEDS][32]; |
95 | atomic_t brightness[NO_LEDS]; | 73 | atomic_t brightness[NO_LEDS]; |
96 | unsigned long brightness_new; | 74 | unsigned long brightness_new; |
75 | #endif | ||
97 | 76 | ||
98 | u8 *transfer_buffer; | 77 | u8 *transfer_buffer; |
99 | u32 last_val; | 78 | u32 last_val; |
@@ -175,20 +154,13 @@ static struct snd_tea575x_ops shark_tea_ops = { | |||
175 | .read_val = shark_read_val, | 154 | .read_val = shark_read_val, |
176 | }; | 155 | }; |
177 | 156 | ||
157 | #ifdef SHARK_USE_LEDS | ||
178 | static void shark_led_work(struct work_struct *work) | 158 | static void shark_led_work(struct work_struct *work) |
179 | { | 159 | { |
180 | struct shark_device *shark = | 160 | struct shark_device *shark = |
181 | container_of(work, struct shark_device, led_work); | 161 | container_of(work, struct shark_device, led_work); |
182 | int i, res, brightness, actual_len; | 162 | int i, res, brightness, actual_len; |
183 | 163 | ||
184 | /* | ||
185 | * We use the v4l2_dev lock and registered bit to ensure the device | ||
186 | * does not get unplugged and unreffed while we're running. | ||
187 | */ | ||
188 | mutex_lock(&shark->tea.mutex); | ||
189 | if (!video_is_registered(&shark->tea.vd)) | ||
190 | goto leave; | ||
191 | |||
192 | for (i = 0; i < 3; i++) { | 164 | for (i = 0; i < 3; i++) { |
193 | if (!test_and_clear_bit(i, &shark->brightness_new)) | 165 | if (!test_and_clear_bit(i, &shark->brightness_new)) |
194 | continue; | 166 | continue; |
@@ -208,8 +180,6 @@ static void shark_led_work(struct work_struct *work) | |||
208 | v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n", | 180 | v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n", |
209 | shark->led_names[i], res); | 181 | shark->led_names[i], res); |
210 | } | 182 | } |
211 | leave: | ||
212 | mutex_unlock(&shark->tea.mutex); | ||
213 | } | 183 | } |
214 | 184 | ||
215 | static void shark_led_set_blue(struct led_classdev *led_cdev, | 185 | static void shark_led_set_blue(struct led_classdev *led_cdev, |
@@ -245,19 +215,78 @@ static void shark_led_set_red(struct led_classdev *led_cdev, | |||
245 | schedule_work(&shark->led_work); | 215 | schedule_work(&shark->led_work); |
246 | } | 216 | } |
247 | 217 | ||
218 | static const struct led_classdev shark_led_templates[NO_LEDS] = { | ||
219 | [BLUE_LED] = { | ||
220 | .name = "%s:blue:", | ||
221 | .brightness = LED_OFF, | ||
222 | .max_brightness = 127, | ||
223 | .brightness_set = shark_led_set_blue, | ||
224 | }, | ||
225 | [BLUE_PULSE_LED] = { | ||
226 | .name = "%s:blue-pulse:", | ||
227 | .brightness = LED_OFF, | ||
228 | .max_brightness = 255, | ||
229 | .brightness_set = shark_led_set_blue_pulse, | ||
230 | }, | ||
231 | [RED_LED] = { | ||
232 | .name = "%s:red:", | ||
233 | .brightness = LED_OFF, | ||
234 | .max_brightness = 1, | ||
235 | .brightness_set = shark_led_set_red, | ||
236 | }, | ||
237 | }; | ||
238 | |||
239 | static int shark_register_leds(struct shark_device *shark, struct device *dev) | ||
240 | { | ||
241 | int i, retval; | ||
242 | |||
243 | INIT_WORK(&shark->led_work, shark_led_work); | ||
244 | for (i = 0; i < NO_LEDS; i++) { | ||
245 | shark->leds[i] = shark_led_templates[i]; | ||
246 | snprintf(shark->led_names[i], sizeof(shark->led_names[0]), | ||
247 | shark->leds[i].name, shark->v4l2_dev.name); | ||
248 | shark->leds[i].name = shark->led_names[i]; | ||
249 | retval = led_classdev_register(dev, &shark->leds[i]); | ||
250 | if (retval) { | ||
251 | v4l2_err(&shark->v4l2_dev, | ||
252 | "couldn't register led: %s\n", | ||
253 | shark->led_names[i]); | ||
254 | return retval; | ||
255 | } | ||
256 | } | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static void shark_unregister_leds(struct shark_device *shark) | ||
261 | { | ||
262 | int i; | ||
263 | |||
264 | for (i = 0; i < NO_LEDS; i++) | ||
265 | led_classdev_unregister(&shark->leds[i]); | ||
266 | |||
267 | cancel_work_sync(&shark->led_work); | ||
268 | } | ||
269 | #else | ||
270 | static int shark_register_leds(struct shark_device *shark, struct device *dev) | ||
271 | { | ||
272 | v4l2_warn(&shark->v4l2_dev, | ||
273 | "CONFIG_LED_CLASS not enabled, LED support disabled\n"); | ||
274 | return 0; | ||
275 | } | ||
276 | static inline void shark_unregister_leds(struct shark_device *shark) { } | ||
277 | #endif | ||
278 | |||
248 | static void usb_shark_disconnect(struct usb_interface *intf) | 279 | static void usb_shark_disconnect(struct usb_interface *intf) |
249 | { | 280 | { |
250 | struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); | 281 | struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); |
251 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); | 282 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); |
252 | int i; | ||
253 | 283 | ||
254 | mutex_lock(&shark->tea.mutex); | 284 | mutex_lock(&shark->tea.mutex); |
255 | v4l2_device_disconnect(&shark->v4l2_dev); | 285 | v4l2_device_disconnect(&shark->v4l2_dev); |
256 | snd_tea575x_exit(&shark->tea); | 286 | snd_tea575x_exit(&shark->tea); |
257 | mutex_unlock(&shark->tea.mutex); | 287 | mutex_unlock(&shark->tea.mutex); |
258 | 288 | ||
259 | for (i = 0; i < NO_LEDS; i++) | 289 | shark_unregister_leds(shark); |
260 | led_classdev_unregister(&shark->leds[i]); | ||
261 | 290 | ||
262 | v4l2_device_put(&shark->v4l2_dev); | 291 | v4l2_device_put(&shark->v4l2_dev); |
263 | } | 292 | } |
@@ -266,7 +295,6 @@ static void usb_shark_release(struct v4l2_device *v4l2_dev) | |||
266 | { | 295 | { |
267 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); | 296 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); |
268 | 297 | ||
269 | cancel_work_sync(&shark->led_work); | ||
270 | v4l2_device_unregister(&shark->v4l2_dev); | 298 | v4l2_device_unregister(&shark->v4l2_dev); |
271 | kfree(shark->transfer_buffer); | 299 | kfree(shark->transfer_buffer); |
272 | kfree(shark); | 300 | kfree(shark); |
@@ -276,7 +304,7 @@ static int usb_shark_probe(struct usb_interface *intf, | |||
276 | const struct usb_device_id *id) | 304 | const struct usb_device_id *id) |
277 | { | 305 | { |
278 | struct shark_device *shark; | 306 | struct shark_device *shark; |
279 | int i, retval = -ENOMEM; | 307 | int retval = -ENOMEM; |
280 | 308 | ||
281 | shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL); | 309 | shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL); |
282 | if (!shark) | 310 | if (!shark) |
@@ -286,17 +314,13 @@ static int usb_shark_probe(struct usb_interface *intf, | |||
286 | if (!shark->transfer_buffer) | 314 | if (!shark->transfer_buffer) |
287 | goto err_alloc_buffer; | 315 | goto err_alloc_buffer; |
288 | 316 | ||
289 | /* | 317 | v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance); |
290 | * Work around a bug in usbhid/hid-core.c, where it leaves a dangling | 318 | |
291 | * pointer in intfdata causing v4l2-device.c to not set it. Which | 319 | retval = shark_register_leds(shark, &intf->dev); |
292 | * results in usb_shark_disconnect() referencing the dangling pointer | 320 | if (retval) |
293 | * | 321 | goto err_reg_leds; |
294 | * REMOVE (as soon as the above bug is fixed, patch submitted) | ||
295 | */ | ||
296 | usb_set_intfdata(intf, NULL); | ||
297 | 322 | ||
298 | shark->v4l2_dev.release = usb_shark_release; | 323 | shark->v4l2_dev.release = usb_shark_release; |
299 | v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance); | ||
300 | retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev); | 324 | retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev); |
301 | if (retval) { | 325 | if (retval) { |
302 | v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n"); | 326 | v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n"); |
@@ -320,32 +344,13 @@ static int usb_shark_probe(struct usb_interface *intf, | |||
320 | goto err_init_tea; | 344 | goto err_init_tea; |
321 | } | 345 | } |
322 | 346 | ||
323 | INIT_WORK(&shark->led_work, shark_led_work); | ||
324 | for (i = 0; i < NO_LEDS; i++) { | ||
325 | shark->leds[i] = shark_led_templates[i]; | ||
326 | snprintf(shark->led_names[i], sizeof(shark->led_names[0]), | ||
327 | shark->leds[i].name, shark->v4l2_dev.name); | ||
328 | shark->leds[i].name = shark->led_names[i]; | ||
329 | /* | ||
330 | * We don't fail the probe if we fail to register the leds, | ||
331 | * because once we've called snd_tea575x_init, the /dev/radio0 | ||
332 | * node may be opened from userspace holding a reference to us! | ||
333 | * | ||
334 | * Note we cannot register the leds first instead as | ||
335 | * shark_led_work depends on the v4l2 mutex and registered bit. | ||
336 | */ | ||
337 | retval = led_classdev_register(&intf->dev, &shark->leds[i]); | ||
338 | if (retval) | ||
339 | v4l2_err(&shark->v4l2_dev, | ||
340 | "couldn't register led: %s\n", | ||
341 | shark->led_names[i]); | ||
342 | } | ||
343 | |||
344 | return 0; | 347 | return 0; |
345 | 348 | ||
346 | err_init_tea: | 349 | err_init_tea: |
347 | v4l2_device_unregister(&shark->v4l2_dev); | 350 | v4l2_device_unregister(&shark->v4l2_dev); |
348 | err_reg_dev: | 351 | err_reg_dev: |
352 | shark_unregister_leds(shark); | ||
353 | err_reg_leds: | ||
349 | kfree(shark->transfer_buffer); | 354 | kfree(shark->transfer_buffer); |
350 | err_alloc_buffer: | 355 | err_alloc_buffer: |
351 | kfree(shark); | 356 | kfree(shark); |