summaryrefslogtreecommitdiff
path: root/drivers/media/usb/tlg2300/pd-main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/usb/tlg2300/pd-main.c')
-rw-r--r--drivers/media/usb/tlg2300/pd-main.c553
1 files changed, 0 insertions, 553 deletions
diff --git a/drivers/media/usb/tlg2300/pd-main.c b/drivers/media/usb/tlg2300/pd-main.c
deleted file mode 100644
index b31f4791b8ff..000000000000
--- a/drivers/media/usb/tlg2300/pd-main.c
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * device driver for Telegent tlg2300 based TV cards
- *
- * Author :
- * Kang Yong <kangyong@telegent.com>
- * Zhang Xiaobing <xbzhang@telegent.com>
- * Huang Shijie <zyziii@telegent.com> or <shijie8@gmail.com>
- *
- * (c) 2009 Telegent Systems
- * (c) 2010 Telegent Systems
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/kref.h>
-#include <linux/suspend.h>
-#include <linux/usb/quirks.h>
-#include <linux/ctype.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/firmware.h>
-
-#include "vendorcmds.h"
-#include "pd-common.h"
-
-#define VENDOR_ID 0x1B24
-#define PRODUCT_ID 0x4001
-static struct usb_device_id id_table[] = {
- { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) },
- { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) },
- { },
-};
-MODULE_DEVICE_TABLE(usb, id_table);
-
-int debug_mode;
-module_param(debug_mode, int, 0644);
-MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose");
-
-#define TLG2300_FIRMWARE "tlg2300_firmware.bin"
-static const char *firmware_name = TLG2300_FIRMWARE;
-static LIST_HEAD(pd_device_list);
-
-/*
- * send set request to USB firmware.
- */
-s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status)
-{
- s32 ret;
- s8 data[32] = {};
- u16 lower_16, upper_16;
-
- if (pd->state & POSEIDON_STATE_DISCONNECT)
- return -ENODEV;
-
- mdelay(30);
-
- if (param == 0) {
- upper_16 = lower_16 = 0;
- } else {
- /* send 32 bit param as two 16 bit param,little endian */
- lower_16 = (unsigned short)(param & 0xffff);
- upper_16 = (unsigned short)((param >> 16) & 0xffff);
- }
- ret = usb_control_msg(pd->udev,
- usb_rcvctrlpipe(pd->udev, 0),
- REQ_SET_CMD | cmdid,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- lower_16,
- upper_16,
- &data,
- sizeof(*cmd_status),
- USB_CTRL_GET_TIMEOUT);
-
- if (!ret) {
- return -ENXIO;
- } else {
- /* 1st 4 bytes into cmd_status */
- memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status));
- }
- return 0;
-}
-
-/*
- * send get request to Poseidon firmware.
- */
-s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param,
- void *buf, s32 *cmd_status, s32 datalen)
-{
- s32 ret;
- s8 data[128] = {};
- u16 lower_16, upper_16;
-
- if (pd->state & POSEIDON_STATE_DISCONNECT)
- return -ENODEV;
-
- mdelay(30);
- if (param == 0) {
- upper_16 = lower_16 = 0;
- } else {
- /*send 32 bit param as two 16 bit param, little endian */
- lower_16 = (unsigned short)(param & 0xffff);
- upper_16 = (unsigned short)((param >> 16) & 0xffff);
- }
- ret = usb_control_msg(pd->udev,
- usb_rcvctrlpipe(pd->udev, 0),
- REQ_GET_CMD | cmdid,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- lower_16,
- upper_16,
- &data,
- (datalen + sizeof(*cmd_status)),
- USB_CTRL_GET_TIMEOUT);
-
- if (ret < 0) {
- return -ENXIO;
- } else {
- /* 1st 4 bytes into cmd_status, remaining data into cmd_data */
- memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status));
- memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen);
- }
- return 0;
-}
-
-static int pm_notifier_block(struct notifier_block *nb,
- unsigned long event, void *dummy)
-{
- struct poseidon *pd = NULL;
- struct list_head *node, *next;
-
- switch (event) {
- case PM_POST_HIBERNATION:
- list_for_each_safe(node, next, &pd_device_list) {
- struct usb_device *udev;
- struct usb_interface *iface;
- int rc = 0;
-
- pd = container_of(node, struct poseidon, device_list);
- udev = pd->udev;
- iface = pd->interface;
-
- /* It will cause the system to reload the firmware */
- rc = usb_lock_device_for_reset(udev, iface);
- if (rc >= 0) {
- usb_reset_device(udev);
- usb_unlock_device(udev);
- }
- }
- break;
- default:
- break;
- }
- log("event :%ld\n", event);
- return 0;
-}
-
-static struct notifier_block pm_notifer = {
- .notifier_call = pm_notifier_block,
-};
-
-int set_tuner_mode(struct poseidon *pd, unsigned char mode)
-{
- s32 ret, cmd_status;
-
- if (pd->state & POSEIDON_STATE_DISCONNECT)
- return -ENODEV;
-
- ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status);
- if (ret || cmd_status)
- return -ENXIO;
- return 0;
-}
-
-void poseidon_delete(struct kref *kref)
-{
- struct poseidon *pd = container_of(kref, struct poseidon, kref);
-
- if (!pd)
- return;
- list_del_init(&pd->device_list);
-
- pd_dvb_usb_device_cleanup(pd);
- /* clean_audio_data(&pd->audio_data);*/
-
- if (pd->udev) {
- usb_put_dev(pd->udev);
- pd->udev = NULL;
- }
- if (pd->interface) {
- usb_put_intf(pd->interface);
- pd->interface = NULL;
- }
- kfree(pd);
- log();
-}
-
-static int firmware_download(struct usb_device *udev)
-{
- int ret = 0, actual_length;
- const struct firmware *fw = NULL;
- void *fwbuf = NULL;
- size_t fwlength = 0, offset;
- size_t max_packet_size;
-
- ret = request_firmware(&fw, firmware_name, &udev->dev);
- if (ret) {
- log("download err : %d", ret);
- return ret;
- }
-
- fwlength = fw->size;
-
- fwbuf = kmemdup(fw->data, fwlength, GFP_KERNEL);
- if (!fwbuf) {
- ret = -ENOMEM;
- goto out;
- }
-
- max_packet_size = le16_to_cpu(udev->ep_out[0x1]->desc.wMaxPacketSize);
- log("\t\t download size : %d", (int)max_packet_size);
-
- for (offset = 0; offset < fwlength; offset += max_packet_size) {
- actual_length = 0;
- ret = usb_bulk_msg(udev,
- usb_sndbulkpipe(udev, 0x01), /* ep 1 */
- fwbuf + offset,
- min(max_packet_size, fwlength - offset),
- &actual_length,
- HZ * 10);
- if (ret)
- break;
- }
- kfree(fwbuf);
-out:
- release_firmware(fw);
- return ret;
-}
-
-static inline struct poseidon *get_pd(struct usb_interface *intf)
-{
- return usb_get_intfdata(intf);
-}
-
-#ifdef CONFIG_PM
-/* one-to-one map : poseidon{} <----> usb_device{}'s port */
-static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
-{
- pd->portnum = udev->portnum;
-}
-
-static inline int get_autopm_ref(struct poseidon *pd)
-{
- return pd->video_data.users + pd->vbi_data.users + pd->audio.users
- + atomic_read(&pd->dvb_data.users) +
- !list_empty(&pd->radio_data.fm_dev.fh_list);
-}
-
-/* fixup something for poseidon */
-static inline struct poseidon *fixup(struct poseidon *pd)
-{
- int count;
-
- /* old udev and interface have gone, so put back reference . */
- count = get_autopm_ref(pd);
- log("count : %d, ref count : %d", count, get_pm_count(pd));
- while (count--)
- usb_autopm_put_interface(pd->interface);
- /*usb_autopm_set_interface(pd->interface); */
-
- usb_put_dev(pd->udev);
- usb_put_intf(pd->interface);
- log("event : %d\n", pd->msg.event);
- return pd;
-}
-
-static struct poseidon *find_old_poseidon(struct usb_device *udev)
-{
- struct poseidon *pd;
-
- list_for_each_entry(pd, &pd_device_list, device_list) {
- if (pd->portnum == udev->portnum && in_hibernation(pd))
- return fixup(pd);
- }
- return NULL;
-}
-
-/* Is the card working now ? */
-static inline int is_working(struct poseidon *pd)
-{
- return get_pm_count(pd) > 0;
-}
-
-static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg)
-{
- struct poseidon *pd = get_pd(intf);
-
- if (!pd)
- return 0;
- if (!is_working(pd)) {
- if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) {
- pd->msg.event = PM_EVENT_AUTO_SUSPEND;
- pd->pm_resume = NULL; /* a good guard */
- printk(KERN_DEBUG "TLG2300 auto suspend\n");
- }
- return 0;
- }
- pd->msg = msg; /* save it here */
- logpm(pd);
- return pd->pm_suspend ? pd->pm_suspend(pd) : 0;
-}
-
-static int poseidon_resume(struct usb_interface *intf)
-{
- struct poseidon *pd = get_pd(intf);
-
- if (!pd)
- return 0;
- printk(KERN_DEBUG "TLG2300 resume\n");
-
- if (!is_working(pd)) {
- if (PM_EVENT_AUTO_SUSPEND == pd->msg.event)
- pd->msg = PMSG_ON;
- return 0;
- }
- if (in_hibernation(pd)) {
- logpm(pd);
- return 0;
- }
- logpm(pd);
- return pd->pm_resume ? pd->pm_resume(pd) : 0;
-}
-
-static void hibernation_resume(struct work_struct *w)
-{
- struct poseidon *pd = container_of(w, struct poseidon, pm_work);
- int count;
-
- pd->msg.event = 0; /* clear it here */
- pd->state &= ~POSEIDON_STATE_DISCONNECT;
-
- /* set the new interface's reference */
- count = get_autopm_ref(pd);
- while (count--)
- usb_autopm_get_interface(pd->interface);
-
- /* resume the context */
- logpm(pd);
- if (pd->pm_resume)
- pd->pm_resume(pd);
-}
-#else /* CONFIG_PM is not enabled: */
-static inline struct poseidon *find_old_poseidon(struct usb_device *udev)
-{
- return NULL;
-}
-
-static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
-{
-}
-#endif
-
-static int check_firmware(struct usb_device *udev)
-{
- void *buf;
- int ret;
- struct cmd_firmware_vers_s *cmd_firm;
-
- buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- ret = usb_control_msg(udev,
- usb_rcvctrlpipe(udev, 0),
- REQ_GET_CMD | GET_FW_ID,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0,
- 0,
- buf,
- sizeof(*cmd_firm) + sizeof(u32),
- USB_CTRL_GET_TIMEOUT);
- kfree(buf);
-
- if (ret < 0)
- return firmware_download(udev);
- return 0;
-}
-
-static int poseidon_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(interface);
- struct poseidon *pd = NULL;
- int ret = 0;
- int new_one = 0;
-
- /* download firmware */
- ret = check_firmware(udev);
- if (ret)
- return ret;
-
- /* Do I recovery from the hibernate ? */
- pd = find_old_poseidon(udev);
- if (!pd) {
- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return -ENOMEM;
- kref_init(&pd->kref);
- set_map_flags(pd, udev);
- new_one = 1;
- }
-
- pd->udev = usb_get_dev(udev);
- pd->interface = usb_get_intf(interface);
- usb_set_intfdata(interface, pd);
-
- if (new_one) {
- logpm(pd);
- mutex_init(&pd->lock);
-
- /* register v4l2 device */
- ret = v4l2_device_register(&interface->dev, &pd->v4l2_dev);
- if (ret)
- goto err_v4l2;
-
- /* register devices in directory /dev */
- ret = pd_video_init(pd);
- if (ret)
- goto err_video;
- ret = poseidon_audio_init(pd);
- if (ret)
- goto err_audio;
- ret = poseidon_fm_init(pd);
- if (ret)
- goto err_fm;
- ret = pd_dvb_usb_device_init(pd);
- if (ret)
- goto err_dvb;
-
- INIT_LIST_HEAD(&pd->device_list);
- list_add_tail(&pd->device_list, &pd_device_list);
- }
-
- device_init_wakeup(&udev->dev, 1);
-#ifdef CONFIG_PM
- pm_runtime_set_autosuspend_delay(&pd->udev->dev,
- 1000 * PM_SUSPEND_DELAY);
- usb_enable_autosuspend(pd->udev);
-
- if (in_hibernation(pd)) {
- INIT_WORK(&pd->pm_work, hibernation_resume);
- schedule_work(&pd->pm_work);
- }
-#endif
- return 0;
-err_dvb:
- poseidon_fm_exit(pd);
-err_fm:
- poseidon_audio_free(pd);
-err_audio:
- pd_video_exit(pd);
-err_video:
- v4l2_device_unregister(&pd->v4l2_dev);
-err_v4l2:
- usb_put_intf(pd->interface);
- usb_put_dev(pd->udev);
- kfree(pd);
- return ret;
-}
-
-static void poseidon_disconnect(struct usb_interface *interface)
-{
- struct poseidon *pd = get_pd(interface);
-
- if (!pd)
- return;
- logpm(pd);
- if (in_hibernation(pd))
- return;
-
- mutex_lock(&pd->lock);
- pd->state |= POSEIDON_STATE_DISCONNECT;
- mutex_unlock(&pd->lock);
-
- /* stop urb transferring */
- stop_all_video_stream(pd);
- dvb_stop_streaming(&pd->dvb_data);
-
- /*unregister v4l2 device */
- v4l2_device_unregister(&pd->v4l2_dev);
-
- pd_dvb_usb_device_exit(pd);
- poseidon_fm_exit(pd);
-
- poseidon_audio_free(pd);
- pd_video_exit(pd);
-
- usb_set_intfdata(interface, NULL);
- kref_put(&pd->kref, poseidon_delete);
-}
-
-static struct usb_driver poseidon_driver = {
- .name = "poseidon",
- .probe = poseidon_probe,
- .disconnect = poseidon_disconnect,
- .id_table = id_table,
-#ifdef CONFIG_PM
- .suspend = poseidon_suspend,
- .resume = poseidon_resume,
-#endif
- .supports_autosuspend = 1,
-};
-
-static int __init poseidon_init(void)
-{
- int ret;
-
- ret = usb_register(&poseidon_driver);
- if (ret)
- return ret;
- register_pm_notifier(&pm_notifer);
- return ret;
-}
-
-static void __exit poseidon_exit(void)
-{
- log();
- unregister_pm_notifier(&pm_notifer);
- usb_deregister(&poseidon_driver);
-}
-
-module_init(poseidon_init);
-module_exit(poseidon_exit);
-
-MODULE_AUTHOR("Telegent Systems");
-MODULE_DESCRIPTION("For tlg2300-based USB device");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.2");
-MODULE_FIRMWARE(TLG2300_FIRMWARE);