10c0d06caSMauro Carvalho Chehab /* 20c0d06caSMauro Carvalho Chehab * Driver for the Auvitek USB bridge 30c0d06caSMauro Carvalho Chehab * 40c0d06caSMauro Carvalho Chehab * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> 50c0d06caSMauro Carvalho Chehab * 60c0d06caSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 70c0d06caSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 80c0d06caSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 90c0d06caSMauro Carvalho Chehab * (at your option) any later version. 100c0d06caSMauro Carvalho Chehab * 110c0d06caSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 120c0d06caSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 130c0d06caSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 140c0d06caSMauro Carvalho Chehab * 150c0d06caSMauro Carvalho Chehab * GNU General Public License for more details. 160c0d06caSMauro Carvalho Chehab * 170c0d06caSMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 180c0d06caSMauro Carvalho Chehab * along with this program; if not, write to the Free Software 190c0d06caSMauro Carvalho Chehab * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 200c0d06caSMauro Carvalho Chehab */ 210c0d06caSMauro Carvalho Chehab 2283afb32aSMauro Carvalho Chehab #include "au0828.h" 2383afb32aSMauro Carvalho Chehab 240c0d06caSMauro Carvalho Chehab #include <linux/module.h> 250c0d06caSMauro Carvalho Chehab #include <linux/slab.h> 260c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h> 270c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h> 280c0d06caSMauro Carvalho Chehab #include <linux/mutex.h> 290c0d06caSMauro Carvalho Chehab 30188d2d55SMauro Carvalho Chehab /* Due to enum tuner_pad_index */ 31188d2d55SMauro Carvalho Chehab #include <media/tuner.h> 32188d2d55SMauro Carvalho Chehab 330c0d06caSMauro Carvalho Chehab /* 340c0d06caSMauro Carvalho Chehab * 1 = General debug messages 350c0d06caSMauro Carvalho Chehab * 2 = USB handling 360c0d06caSMauro Carvalho Chehab * 4 = I2C related 370c0d06caSMauro Carvalho Chehab * 8 = Bridge related 382fcfd317SMauro Carvalho Chehab * 16 = IR related 390c0d06caSMauro Carvalho Chehab */ 400c0d06caSMauro Carvalho Chehab int au0828_debug; 410c0d06caSMauro Carvalho Chehab module_param_named(debug, au0828_debug, int, 0644); 422fcfd317SMauro Carvalho Chehab MODULE_PARM_DESC(debug, 432fcfd317SMauro Carvalho Chehab "set debug bitmask: 1=general, 2=USB, 4=I2C, 8=bridge, 16=IR"); 440c0d06caSMauro Carvalho Chehab 450c0d06caSMauro Carvalho Chehab static unsigned int disable_usb_speed_check; 460c0d06caSMauro Carvalho Chehab module_param(disable_usb_speed_check, int, 0444); 470c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(disable_usb_speed_check, 480c0d06caSMauro Carvalho Chehab "override min bandwidth requirement of 480M bps"); 490c0d06caSMauro Carvalho Chehab 500c0d06caSMauro Carvalho Chehab #define _AU0828_BULKPIPE 0x03 510c0d06caSMauro Carvalho Chehab #define _BULKPIPESIZE 0xffff 520c0d06caSMauro Carvalho Chehab 530c0d06caSMauro Carvalho Chehab static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, 540c0d06caSMauro Carvalho Chehab u16 index); 550c0d06caSMauro Carvalho Chehab static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, 560c0d06caSMauro Carvalho Chehab u16 index, unsigned char *cp, u16 size); 570c0d06caSMauro Carvalho Chehab 580c0d06caSMauro Carvalho Chehab /* USB Direction */ 590c0d06caSMauro Carvalho Chehab #define CMD_REQUEST_IN 0x00 600c0d06caSMauro Carvalho Chehab #define CMD_REQUEST_OUT 0x01 610c0d06caSMauro Carvalho Chehab 620c0d06caSMauro Carvalho Chehab u32 au0828_readreg(struct au0828_dev *dev, u16 reg) 630c0d06caSMauro Carvalho Chehab { 640c0d06caSMauro Carvalho Chehab u8 result = 0; 650c0d06caSMauro Carvalho Chehab 660c0d06caSMauro Carvalho Chehab recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, &result, 1); 670c0d06caSMauro Carvalho Chehab dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, result); 680c0d06caSMauro Carvalho Chehab 690c0d06caSMauro Carvalho Chehab return result; 700c0d06caSMauro Carvalho Chehab } 710c0d06caSMauro Carvalho Chehab 720c0d06caSMauro Carvalho Chehab u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val) 730c0d06caSMauro Carvalho Chehab { 740c0d06caSMauro Carvalho Chehab dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val); 750c0d06caSMauro Carvalho Chehab return send_control_msg(dev, CMD_REQUEST_OUT, val, reg); 760c0d06caSMauro Carvalho Chehab } 770c0d06caSMauro Carvalho Chehab 780c0d06caSMauro Carvalho Chehab static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, 790c0d06caSMauro Carvalho Chehab u16 index) 800c0d06caSMauro Carvalho Chehab { 810c0d06caSMauro Carvalho Chehab int status = -ENODEV; 820c0d06caSMauro Carvalho Chehab 830c0d06caSMauro Carvalho Chehab if (dev->usbdev) { 840c0d06caSMauro Carvalho Chehab 850c0d06caSMauro Carvalho Chehab /* cp must be memory that has been allocated by kmalloc */ 860c0d06caSMauro Carvalho Chehab status = usb_control_msg(dev->usbdev, 870c0d06caSMauro Carvalho Chehab usb_sndctrlpipe(dev->usbdev, 0), 880c0d06caSMauro Carvalho Chehab request, 890c0d06caSMauro Carvalho Chehab USB_DIR_OUT | USB_TYPE_VENDOR | 900c0d06caSMauro Carvalho Chehab USB_RECIP_DEVICE, 910c0d06caSMauro Carvalho Chehab value, index, NULL, 0, 1000); 920c0d06caSMauro Carvalho Chehab 930c0d06caSMauro Carvalho Chehab status = min(status, 0); 940c0d06caSMauro Carvalho Chehab 950c0d06caSMauro Carvalho Chehab if (status < 0) { 9683afb32aSMauro Carvalho Chehab pr_err("%s() Failed sending control message, error %d.\n", 970c0d06caSMauro Carvalho Chehab __func__, status); 980c0d06caSMauro Carvalho Chehab } 990c0d06caSMauro Carvalho Chehab 1000c0d06caSMauro Carvalho Chehab } 1010c0d06caSMauro Carvalho Chehab 1020c0d06caSMauro Carvalho Chehab return status; 1030c0d06caSMauro Carvalho Chehab } 1040c0d06caSMauro Carvalho Chehab 1050c0d06caSMauro Carvalho Chehab static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, 1060c0d06caSMauro Carvalho Chehab u16 index, unsigned char *cp, u16 size) 1070c0d06caSMauro Carvalho Chehab { 1080c0d06caSMauro Carvalho Chehab int status = -ENODEV; 1090c0d06caSMauro Carvalho Chehab mutex_lock(&dev->mutex); 1100c0d06caSMauro Carvalho Chehab if (dev->usbdev) { 1110c0d06caSMauro Carvalho Chehab status = usb_control_msg(dev->usbdev, 1120c0d06caSMauro Carvalho Chehab usb_rcvctrlpipe(dev->usbdev, 0), 1130c0d06caSMauro Carvalho Chehab request, 1140c0d06caSMauro Carvalho Chehab USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1150c0d06caSMauro Carvalho Chehab value, index, 1160c0d06caSMauro Carvalho Chehab dev->ctrlmsg, size, 1000); 1170c0d06caSMauro Carvalho Chehab 1180c0d06caSMauro Carvalho Chehab status = min(status, 0); 1190c0d06caSMauro Carvalho Chehab 1200c0d06caSMauro Carvalho Chehab if (status < 0) { 12183afb32aSMauro Carvalho Chehab pr_err("%s() Failed receiving control message, error %d.\n", 1220c0d06caSMauro Carvalho Chehab __func__, status); 1230c0d06caSMauro Carvalho Chehab } 1240c0d06caSMauro Carvalho Chehab 1250c0d06caSMauro Carvalho Chehab /* the host controller requires heap allocated memory, which 1260c0d06caSMauro Carvalho Chehab is why we didn't just pass "cp" into usb_control_msg */ 1270c0d06caSMauro Carvalho Chehab memcpy(cp, dev->ctrlmsg, size); 1280c0d06caSMauro Carvalho Chehab } 1290c0d06caSMauro Carvalho Chehab mutex_unlock(&dev->mutex); 1300c0d06caSMauro Carvalho Chehab return status; 1310c0d06caSMauro Carvalho Chehab } 1320c0d06caSMauro Carvalho Chehab 133bed69196SRafael Lourenço de Lima Chehab static void au0828_unregister_media_device(struct au0828_dev *dev) 134bed69196SRafael Lourenço de Lima Chehab { 135bed69196SRafael Lourenço de Lima Chehab 136bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER 137bed69196SRafael Lourenço de Lima Chehab if (dev->media_dev) { 138bed69196SRafael Lourenço de Lima Chehab media_device_unregister(dev->media_dev); 139bed69196SRafael Lourenço de Lima Chehab kfree(dev->media_dev); 140bed69196SRafael Lourenço de Lima Chehab dev->media_dev = NULL; 141bed69196SRafael Lourenço de Lima Chehab } 142bed69196SRafael Lourenço de Lima Chehab #endif 143bed69196SRafael Lourenço de Lima Chehab } 144bed69196SRafael Lourenço de Lima Chehab 145823beb7eSHans Verkuil static void au0828_usb_release(struct au0828_dev *dev) 146823beb7eSHans Verkuil { 147bed69196SRafael Lourenço de Lima Chehab au0828_unregister_media_device(dev); 148bed69196SRafael Lourenço de Lima Chehab 149823beb7eSHans Verkuil /* I2C */ 150823beb7eSHans Verkuil au0828_i2c_unregister(dev); 151823beb7eSHans Verkuil 152823beb7eSHans Verkuil kfree(dev); 153823beb7eSHans Verkuil } 154823beb7eSHans Verkuil 155823beb7eSHans Verkuil #ifdef CONFIG_VIDEO_AU0828_V4L2 156d1f33737SMauro Carvalho Chehab 157d1f33737SMauro Carvalho Chehab static void au0828_usb_v4l2_media_release(struct au0828_dev *dev) 158d1f33737SMauro Carvalho Chehab { 159d1f33737SMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 160d1f33737SMauro Carvalho Chehab int i; 161d1f33737SMauro Carvalho Chehab 162d1f33737SMauro Carvalho Chehab for (i = 0; i < AU0828_MAX_INPUT; i++) { 163d1f33737SMauro Carvalho Chehab if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) 164d1f33737SMauro Carvalho Chehab return; 165d1f33737SMauro Carvalho Chehab media_device_unregister_entity(&dev->input_ent[i]); 166d1f33737SMauro Carvalho Chehab } 167d1f33737SMauro Carvalho Chehab #endif 168d1f33737SMauro Carvalho Chehab } 169d1f33737SMauro Carvalho Chehab 170823beb7eSHans Verkuil static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev) 171823beb7eSHans Verkuil { 172823beb7eSHans Verkuil struct au0828_dev *dev = 173823beb7eSHans Verkuil container_of(v4l2_dev, struct au0828_dev, v4l2_dev); 174823beb7eSHans Verkuil 175823beb7eSHans Verkuil v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl); 176823beb7eSHans Verkuil v4l2_device_unregister(&dev->v4l2_dev); 1774e26f3abSMauro Carvalho Chehab au0828_usb_v4l2_media_release(dev); 178823beb7eSHans Verkuil au0828_usb_release(dev); 179823beb7eSHans Verkuil } 180823beb7eSHans Verkuil #endif 181823beb7eSHans Verkuil 1820c0d06caSMauro Carvalho Chehab static void au0828_usb_disconnect(struct usb_interface *interface) 1830c0d06caSMauro Carvalho Chehab { 1840c0d06caSMauro Carvalho Chehab struct au0828_dev *dev = usb_get_intfdata(interface); 1850c0d06caSMauro Carvalho Chehab 1860c0d06caSMauro Carvalho Chehab dprintk(1, "%s()\n", __func__); 1870c0d06caSMauro Carvalho Chehab 188eb336eabSShuah Khan /* there is a small window after disconnect, before 189eb336eabSShuah Khan dev->usbdev is NULL, for poll (e.g: IR) try to access 190eb336eabSShuah Khan the device and fill the dmesg with error messages. 191eb336eabSShuah Khan Set the status so poll routines can check and avoid 192eb336eabSShuah Khan access after disconnect. 193eb336eabSShuah Khan */ 194eb336eabSShuah Khan dev->dev_state = DEV_DISCONNECTED; 195eb336eabSShuah Khan 1962fcfd317SMauro Carvalho Chehab au0828_rc_unregister(dev); 1970c0d06caSMauro Carvalho Chehab /* Digital TV */ 1980c0d06caSMauro Carvalho Chehab au0828_dvb_unregister(dev); 1990c0d06caSMauro Carvalho Chehab 2000c0d06caSMauro Carvalho Chehab usb_set_intfdata(interface, NULL); 2010c0d06caSMauro Carvalho Chehab mutex_lock(&dev->mutex); 2020c0d06caSMauro Carvalho Chehab dev->usbdev = NULL; 2030c0d06caSMauro Carvalho Chehab mutex_unlock(&dev->mutex); 204823beb7eSHans Verkuil #ifdef CONFIG_VIDEO_AU0828_V4L2 205823beb7eSHans Verkuil if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { 206823beb7eSHans Verkuil au0828_analog_unregister(dev); 207823beb7eSHans Verkuil v4l2_device_disconnect(&dev->v4l2_dev); 208823beb7eSHans Verkuil v4l2_device_put(&dev->v4l2_dev); 2097e9a8ad5SMauro Carvalho Chehab /* 2107e9a8ad5SMauro Carvalho Chehab * No need to call au0828_usb_release() if V4L2 is enabled, 2117e9a8ad5SMauro Carvalho Chehab * as this is already called via au0828_usb_v4l2_release() 2127e9a8ad5SMauro Carvalho Chehab */ 213823beb7eSHans Verkuil return; 214823beb7eSHans Verkuil } 215823beb7eSHans Verkuil #endif 216823beb7eSHans Verkuil au0828_usb_release(dev); 2170c0d06caSMauro Carvalho Chehab } 2180c0d06caSMauro Carvalho Chehab 219bed69196SRafael Lourenço de Lima Chehab static void au0828_media_device_register(struct au0828_dev *dev, 220bed69196SRafael Lourenço de Lima Chehab struct usb_device *udev) 221bed69196SRafael Lourenço de Lima Chehab { 222bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER 223bed69196SRafael Lourenço de Lima Chehab struct media_device *mdev; 224bed69196SRafael Lourenço de Lima Chehab int ret; 225bed69196SRafael Lourenço de Lima Chehab 226bed69196SRafael Lourenço de Lima Chehab mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); 227bed69196SRafael Lourenço de Lima Chehab if (!mdev) 228bed69196SRafael Lourenço de Lima Chehab return; 229bed69196SRafael Lourenço de Lima Chehab 230bed69196SRafael Lourenço de Lima Chehab mdev->dev = &udev->dev; 231bed69196SRafael Lourenço de Lima Chehab 232bed69196SRafael Lourenço de Lima Chehab if (!dev->board.name) 233bed69196SRafael Lourenço de Lima Chehab strlcpy(mdev->model, "unknown au0828", sizeof(mdev->model)); 234bed69196SRafael Lourenço de Lima Chehab else 235bed69196SRafael Lourenço de Lima Chehab strlcpy(mdev->model, dev->board.name, sizeof(mdev->model)); 236bed69196SRafael Lourenço de Lima Chehab if (udev->serial) 237bed69196SRafael Lourenço de Lima Chehab strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial)); 238bed69196SRafael Lourenço de Lima Chehab strcpy(mdev->bus_info, udev->devpath); 239bed69196SRafael Lourenço de Lima Chehab mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); 240bed69196SRafael Lourenço de Lima Chehab mdev->driver_version = LINUX_VERSION_CODE; 241bed69196SRafael Lourenço de Lima Chehab 242bed69196SRafael Lourenço de Lima Chehab ret = media_device_register(mdev); 243bed69196SRafael Lourenço de Lima Chehab if (ret) { 244bed69196SRafael Lourenço de Lima Chehab pr_err( 245bed69196SRafael Lourenço de Lima Chehab "Couldn't create a media device. Error: %d\n", 246bed69196SRafael Lourenço de Lima Chehab ret); 247bed69196SRafael Lourenço de Lima Chehab kfree(mdev); 248bed69196SRafael Lourenço de Lima Chehab return; 249bed69196SRafael Lourenço de Lima Chehab } 250bed69196SRafael Lourenço de Lima Chehab 251bed69196SRafael Lourenço de Lima Chehab dev->media_dev = mdev; 252bed69196SRafael Lourenço de Lima Chehab #endif 253bed69196SRafael Lourenço de Lima Chehab } 254bed69196SRafael Lourenço de Lima Chehab 255bed69196SRafael Lourenço de Lima Chehab 2564e26f3abSMauro Carvalho Chehab static int au0828_create_media_graph(struct au0828_dev *dev) 257bed69196SRafael Lourenço de Lima Chehab { 258bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER 259bed69196SRafael Lourenço de Lima Chehab struct media_device *mdev = dev->media_dev; 260bed69196SRafael Lourenço de Lima Chehab struct media_entity *entity; 261bed69196SRafael Lourenço de Lima Chehab struct media_entity *tuner = NULL, *decoder = NULL; 2624e26f3abSMauro Carvalho Chehab int i, ret; 263bed69196SRafael Lourenço de Lima Chehab 264bed69196SRafael Lourenço de Lima Chehab if (!mdev) 2654e26f3abSMauro Carvalho Chehab return 0; 266bed69196SRafael Lourenço de Lima Chehab 267bed69196SRafael Lourenço de Lima Chehab media_device_for_each_entity(entity, mdev) { 268bed69196SRafael Lourenço de Lima Chehab switch (entity->type) { 269bed69196SRafael Lourenço de Lima Chehab case MEDIA_ENT_T_V4L2_SUBDEV_TUNER: 270bed69196SRafael Lourenço de Lima Chehab tuner = entity; 271bed69196SRafael Lourenço de Lima Chehab break; 272bed69196SRafael Lourenço de Lima Chehab case MEDIA_ENT_T_V4L2_SUBDEV_DECODER: 273bed69196SRafael Lourenço de Lima Chehab decoder = entity; 274bed69196SRafael Lourenço de Lima Chehab break; 275bed69196SRafael Lourenço de Lima Chehab } 276bed69196SRafael Lourenço de Lima Chehab } 277bed69196SRafael Lourenço de Lima Chehab 278bed69196SRafael Lourenço de Lima Chehab /* Analog setup, using tuner as a link */ 279bed69196SRafael Lourenço de Lima Chehab 28028b6ba11SMauro Carvalho Chehab /* Something bad happened! */ 281bed69196SRafael Lourenço de Lima Chehab if (!decoder) 2824e26f3abSMauro Carvalho Chehab return -EINVAL; 283bed69196SRafael Lourenço de Lima Chehab 2844e26f3abSMauro Carvalho Chehab if (tuner) { 2854e26f3abSMauro Carvalho Chehab ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT, 2864e26f3abSMauro Carvalho Chehab decoder, 0, 287bed69196SRafael Lourenço de Lima Chehab MEDIA_LNK_FL_ENABLED); 2884e26f3abSMauro Carvalho Chehab if (ret) 2894e26f3abSMauro Carvalho Chehab return ret; 2904e26f3abSMauro Carvalho Chehab } 2914e26f3abSMauro Carvalho Chehab ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0, 292bed69196SRafael Lourenço de Lima Chehab MEDIA_LNK_FL_ENABLED); 2934e26f3abSMauro Carvalho Chehab if (ret) 2944e26f3abSMauro Carvalho Chehab return ret; 2954e26f3abSMauro Carvalho Chehab ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0, 296bed69196SRafael Lourenço de Lima Chehab MEDIA_LNK_FL_ENABLED); 2974e26f3abSMauro Carvalho Chehab if (ret) 2984e26f3abSMauro Carvalho Chehab return ret; 29928b6ba11SMauro Carvalho Chehab 30028b6ba11SMauro Carvalho Chehab for (i = 0; i < AU0828_MAX_INPUT; i++) { 30128b6ba11SMauro Carvalho Chehab struct media_entity *ent = &dev->input_ent[i]; 30228b6ba11SMauro Carvalho Chehab 30328b6ba11SMauro Carvalho Chehab if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) 30428b6ba11SMauro Carvalho Chehab break; 30528b6ba11SMauro Carvalho Chehab 30628b6ba11SMauro Carvalho Chehab switch (AUVI_INPUT(i).type) { 30728b6ba11SMauro Carvalho Chehab case AU0828_VMUX_CABLE: 30828b6ba11SMauro Carvalho Chehab case AU0828_VMUX_TELEVISION: 30928b6ba11SMauro Carvalho Chehab case AU0828_VMUX_DVB: 3104e26f3abSMauro Carvalho Chehab if (!tuner) 3114e26f3abSMauro Carvalho Chehab break; 3124e26f3abSMauro Carvalho Chehab 3134e26f3abSMauro Carvalho Chehab ret = media_create_pad_link(ent, 0, tuner, 31428b6ba11SMauro Carvalho Chehab TUNER_PAD_RF_INPUT, 31528b6ba11SMauro Carvalho Chehab MEDIA_LNK_FL_ENABLED); 3164e26f3abSMauro Carvalho Chehab if (ret) 3174e26f3abSMauro Carvalho Chehab return ret; 31828b6ba11SMauro Carvalho Chehab break; 31928b6ba11SMauro Carvalho Chehab case AU0828_VMUX_COMPOSITE: 32028b6ba11SMauro Carvalho Chehab case AU0828_VMUX_SVIDEO: 32128b6ba11SMauro Carvalho Chehab default: /* AU0828_VMUX_DEBUG */ 32228b6ba11SMauro Carvalho Chehab /* FIXME: fix the decoder PAD */ 3234e26f3abSMauro Carvalho Chehab ret = media_create_pad_link(ent, 0, decoder, 0, 0); 3244e26f3abSMauro Carvalho Chehab if (ret) 3254e26f3abSMauro Carvalho Chehab return ret; 32628b6ba11SMauro Carvalho Chehab break; 32728b6ba11SMauro Carvalho Chehab } 32828b6ba11SMauro Carvalho Chehab } 329bed69196SRafael Lourenço de Lima Chehab #endif 3304e26f3abSMauro Carvalho Chehab return 0; 331bed69196SRafael Lourenço de Lima Chehab } 332bed69196SRafael Lourenço de Lima Chehab 3330c0d06caSMauro Carvalho Chehab static int au0828_usb_probe(struct usb_interface *interface, 3340c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 3350c0d06caSMauro Carvalho Chehab { 3368a4e7866SMichael Krufky int ifnum; 337f251b3e7STim Mester int retval = 0; 338f251b3e7STim Mester 3390c0d06caSMauro Carvalho Chehab struct au0828_dev *dev; 3400c0d06caSMauro Carvalho Chehab struct usb_device *usbdev = interface_to_usbdev(interface); 3410c0d06caSMauro Carvalho Chehab 3420c0d06caSMauro Carvalho Chehab ifnum = interface->altsetting->desc.bInterfaceNumber; 3430c0d06caSMauro Carvalho Chehab 3440c0d06caSMauro Carvalho Chehab if (ifnum != 0) 3450c0d06caSMauro Carvalho Chehab return -ENODEV; 3460c0d06caSMauro Carvalho Chehab 3470c0d06caSMauro Carvalho Chehab dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__, 3480c0d06caSMauro Carvalho Chehab le16_to_cpu(usbdev->descriptor.idVendor), 3490c0d06caSMauro Carvalho Chehab le16_to_cpu(usbdev->descriptor.idProduct), 3500c0d06caSMauro Carvalho Chehab ifnum); 3510c0d06caSMauro Carvalho Chehab 3520c0d06caSMauro Carvalho Chehab /* 3530c0d06caSMauro Carvalho Chehab * Make sure we have 480 Mbps of bandwidth, otherwise things like 3540c0d06caSMauro Carvalho Chehab * video stream wouldn't likely work, since 12 Mbps is generally 3550c0d06caSMauro Carvalho Chehab * not enough even for most Digital TV streams. 3560c0d06caSMauro Carvalho Chehab */ 3570c0d06caSMauro Carvalho Chehab if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) { 35883afb32aSMauro Carvalho Chehab pr_err("au0828: Device initialization failed.\n"); 35983afb32aSMauro Carvalho Chehab pr_err("au0828: Device must be connected to a high-speed USB 2.0 port.\n"); 3600c0d06caSMauro Carvalho Chehab return -ENODEV; 3610c0d06caSMauro Carvalho Chehab } 3620c0d06caSMauro Carvalho Chehab 3630c0d06caSMauro Carvalho Chehab dev = kzalloc(sizeof(*dev), GFP_KERNEL); 3640c0d06caSMauro Carvalho Chehab if (dev == NULL) { 36583afb32aSMauro Carvalho Chehab pr_err("%s() Unable to allocate memory\n", __func__); 3660c0d06caSMauro Carvalho Chehab return -ENOMEM; 3670c0d06caSMauro Carvalho Chehab } 3680c0d06caSMauro Carvalho Chehab 3690c0d06caSMauro Carvalho Chehab mutex_init(&dev->lock); 3700c0d06caSMauro Carvalho Chehab mutex_lock(&dev->lock); 3710c0d06caSMauro Carvalho Chehab mutex_init(&dev->mutex); 3720c0d06caSMauro Carvalho Chehab mutex_init(&dev->dvb.lock); 3730c0d06caSMauro Carvalho Chehab dev->usbdev = usbdev; 3740c0d06caSMauro Carvalho Chehab dev->boardnr = id->driver_info; 375e42c8c6eSRafael Lourenço de Lima Chehab dev->board = au0828_boards[dev->boardnr]; 376e42c8c6eSRafael Lourenço de Lima Chehab 377bed69196SRafael Lourenço de Lima Chehab /* Register the media controller */ 378bed69196SRafael Lourenço de Lima Chehab au0828_media_device_register(dev, usbdev); 3790c0d06caSMauro Carvalho Chehab 3808a4e7866SMichael Krufky #ifdef CONFIG_VIDEO_AU0828_V4L2 381823beb7eSHans Verkuil dev->v4l2_dev.release = au0828_usb_v4l2_release; 382823beb7eSHans Verkuil 3830c0d06caSMauro Carvalho Chehab /* Create the v4l2_device */ 384bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER 385bed69196SRafael Lourenço de Lima Chehab dev->v4l2_dev.mdev = dev->media_dev; 386bed69196SRafael Lourenço de Lima Chehab #endif 3870c0d06caSMauro Carvalho Chehab retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); 3880c0d06caSMauro Carvalho Chehab if (retval) { 389e8c26f45SHans Verkuil pr_err("%s() v4l2_device_register failed\n", 3900c0d06caSMauro Carvalho Chehab __func__); 3910c0d06caSMauro Carvalho Chehab mutex_unlock(&dev->lock); 3920c0d06caSMauro Carvalho Chehab kfree(dev); 393e8c26f45SHans Verkuil return retval; 3940c0d06caSMauro Carvalho Chehab } 395e8c26f45SHans Verkuil /* This control handler will inherit the controls from au8522 */ 396e8c26f45SHans Verkuil retval = v4l2_ctrl_handler_init(&dev->v4l2_ctrl_hdl, 4); 397e8c26f45SHans Verkuil if (retval) { 398e8c26f45SHans Verkuil pr_err("%s() v4l2_ctrl_handler_init failed\n", 399e8c26f45SHans Verkuil __func__); 400e8c26f45SHans Verkuil mutex_unlock(&dev->lock); 401e8c26f45SHans Verkuil kfree(dev); 402e8c26f45SHans Verkuil return retval; 403e8c26f45SHans Verkuil } 404e8c26f45SHans Verkuil dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl; 4058a4e7866SMichael Krufky #endif 4060c0d06caSMauro Carvalho Chehab 4070c0d06caSMauro Carvalho Chehab /* Power Up the bridge */ 4080c0d06caSMauro Carvalho Chehab au0828_write(dev, REG_600, 1 << 4); 4090c0d06caSMauro Carvalho Chehab 4100c0d06caSMauro Carvalho Chehab /* Bring up the GPIO's and supporting devices */ 4110c0d06caSMauro Carvalho Chehab au0828_gpio_setup(dev); 4120c0d06caSMauro Carvalho Chehab 4130c0d06caSMauro Carvalho Chehab /* I2C */ 4140c0d06caSMauro Carvalho Chehab au0828_i2c_register(dev); 4150c0d06caSMauro Carvalho Chehab 4160c0d06caSMauro Carvalho Chehab /* Setup */ 4170c0d06caSMauro Carvalho Chehab au0828_card_setup(dev); 4180c0d06caSMauro Carvalho Chehab 4198a4e7866SMichael Krufky #ifdef CONFIG_VIDEO_AU0828_V4L2 4200c0d06caSMauro Carvalho Chehab /* Analog TV */ 4210c0d06caSMauro Carvalho Chehab if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) 4220c0d06caSMauro Carvalho Chehab au0828_analog_register(dev, interface); 4238a4e7866SMichael Krufky #endif 4240c0d06caSMauro Carvalho Chehab 4250c0d06caSMauro Carvalho Chehab /* Digital TV */ 426f251b3e7STim Mester retval = au0828_dvb_register(dev); 427f251b3e7STim Mester if (retval) 428f251b3e7STim Mester pr_err("%s() au0282_dev_register failed\n", 429f251b3e7STim Mester __func__); 430f251b3e7STim Mester 4312fcfd317SMauro Carvalho Chehab /* Remote controller */ 4322fcfd317SMauro Carvalho Chehab au0828_rc_register(dev); 4330c0d06caSMauro Carvalho Chehab 4342fcfd317SMauro Carvalho Chehab /* 4352fcfd317SMauro Carvalho Chehab * Store the pointer to the au0828_dev so it can be accessed in 4362fcfd317SMauro Carvalho Chehab * au0828_usb_disconnect 4372fcfd317SMauro Carvalho Chehab */ 4380c0d06caSMauro Carvalho Chehab usb_set_intfdata(interface, dev); 4390c0d06caSMauro Carvalho Chehab 44083afb32aSMauro Carvalho Chehab pr_info("Registered device AU0828 [%s]\n", 4410c0d06caSMauro Carvalho Chehab dev->board.name == NULL ? "Unset" : dev->board.name); 4420c0d06caSMauro Carvalho Chehab 4430c0d06caSMauro Carvalho Chehab mutex_unlock(&dev->lock); 4440c0d06caSMauro Carvalho Chehab 4454e26f3abSMauro Carvalho Chehab retval = au0828_create_media_graph(dev); 4464e26f3abSMauro Carvalho Chehab if (retval) { 4474e26f3abSMauro Carvalho Chehab pr_err("%s() au0282_dev_register failed to create graph\n", 4484e26f3abSMauro Carvalho Chehab __func__); 4494e26f3abSMauro Carvalho Chehab au0828_usb_disconnect(interface); 4504e26f3abSMauro Carvalho Chehab } 451bed69196SRafael Lourenço de Lima Chehab 452f251b3e7STim Mester return retval; 4530c0d06caSMauro Carvalho Chehab } 4540c0d06caSMauro Carvalho Chehab 455aaeac199SMauro Carvalho Chehab static int au0828_suspend(struct usb_interface *interface, 456aaeac199SMauro Carvalho Chehab pm_message_t message) 457aaeac199SMauro Carvalho Chehab { 458aaeac199SMauro Carvalho Chehab struct au0828_dev *dev = usb_get_intfdata(interface); 459aaeac199SMauro Carvalho Chehab 460aaeac199SMauro Carvalho Chehab if (!dev) 461aaeac199SMauro Carvalho Chehab return 0; 462aaeac199SMauro Carvalho Chehab 46381187240SMauro Carvalho Chehab pr_info("Suspend\n"); 46481187240SMauro Carvalho Chehab 465aaeac199SMauro Carvalho Chehab au0828_rc_suspend(dev); 4661a1ba95eSMauro Carvalho Chehab au0828_v4l2_suspend(dev); 467b799de75SMauro Carvalho Chehab au0828_dvb_suspend(dev); 468aaeac199SMauro Carvalho Chehab 469aaeac199SMauro Carvalho Chehab /* FIXME: should suspend also ATV/DTV */ 470aaeac199SMauro Carvalho Chehab 471aaeac199SMauro Carvalho Chehab return 0; 472aaeac199SMauro Carvalho Chehab } 473aaeac199SMauro Carvalho Chehab 474aaeac199SMauro Carvalho Chehab static int au0828_resume(struct usb_interface *interface) 475aaeac199SMauro Carvalho Chehab { 476aaeac199SMauro Carvalho Chehab struct au0828_dev *dev = usb_get_intfdata(interface); 477aaeac199SMauro Carvalho Chehab if (!dev) 478aaeac199SMauro Carvalho Chehab return 0; 479aaeac199SMauro Carvalho Chehab 48081187240SMauro Carvalho Chehab pr_info("Resume\n"); 48181187240SMauro Carvalho Chehab 482fa500461SMauro Carvalho Chehab /* Power Up the bridge */ 483fa500461SMauro Carvalho Chehab au0828_write(dev, REG_600, 1 << 4); 484fa500461SMauro Carvalho Chehab 485fa500461SMauro Carvalho Chehab /* Bring up the GPIO's and supporting devices */ 486fa500461SMauro Carvalho Chehab au0828_gpio_setup(dev); 487fa500461SMauro Carvalho Chehab 488aaeac199SMauro Carvalho Chehab au0828_rc_resume(dev); 4891a1ba95eSMauro Carvalho Chehab au0828_v4l2_resume(dev); 490b799de75SMauro Carvalho Chehab au0828_dvb_resume(dev); 491aaeac199SMauro Carvalho Chehab 492aaeac199SMauro Carvalho Chehab /* FIXME: should resume also ATV/DTV */ 493aaeac199SMauro Carvalho Chehab 494aaeac199SMauro Carvalho Chehab return 0; 495aaeac199SMauro Carvalho Chehab } 496aaeac199SMauro Carvalho Chehab 4970c0d06caSMauro Carvalho Chehab static struct usb_driver au0828_usb_driver = { 49883afb32aSMauro Carvalho Chehab .name = KBUILD_MODNAME, 4990c0d06caSMauro Carvalho Chehab .probe = au0828_usb_probe, 5000c0d06caSMauro Carvalho Chehab .disconnect = au0828_usb_disconnect, 5010c0d06caSMauro Carvalho Chehab .id_table = au0828_usb_id_table, 502aaeac199SMauro Carvalho Chehab .suspend = au0828_suspend, 503aaeac199SMauro Carvalho Chehab .resume = au0828_resume, 504aaeac199SMauro Carvalho Chehab .reset_resume = au0828_resume, 5050c0d06caSMauro Carvalho Chehab }; 5060c0d06caSMauro Carvalho Chehab 5070c0d06caSMauro Carvalho Chehab static int __init au0828_init(void) 5080c0d06caSMauro Carvalho Chehab { 5090c0d06caSMauro Carvalho Chehab int ret; 5100c0d06caSMauro Carvalho Chehab 5110c0d06caSMauro Carvalho Chehab if (au0828_debug & 1) 51283afb32aSMauro Carvalho Chehab pr_info("%s() Debugging is enabled\n", __func__); 5130c0d06caSMauro Carvalho Chehab 5140c0d06caSMauro Carvalho Chehab if (au0828_debug & 2) 51583afb32aSMauro Carvalho Chehab pr_info("%s() USB Debugging is enabled\n", __func__); 5160c0d06caSMauro Carvalho Chehab 5170c0d06caSMauro Carvalho Chehab if (au0828_debug & 4) 51883afb32aSMauro Carvalho Chehab pr_info("%s() I2C Debugging is enabled\n", __func__); 5190c0d06caSMauro Carvalho Chehab 5200c0d06caSMauro Carvalho Chehab if (au0828_debug & 8) 52183afb32aSMauro Carvalho Chehab pr_info("%s() Bridge Debugging is enabled\n", 5220c0d06caSMauro Carvalho Chehab __func__); 5230c0d06caSMauro Carvalho Chehab 5242fcfd317SMauro Carvalho Chehab if (au0828_debug & 16) 52583afb32aSMauro Carvalho Chehab pr_info("%s() IR Debugging is enabled\n", 5262fcfd317SMauro Carvalho Chehab __func__); 5272fcfd317SMauro Carvalho Chehab 52883afb32aSMauro Carvalho Chehab pr_info("au0828 driver loaded\n"); 5290c0d06caSMauro Carvalho Chehab 5300c0d06caSMauro Carvalho Chehab ret = usb_register(&au0828_usb_driver); 5310c0d06caSMauro Carvalho Chehab if (ret) 53283afb32aSMauro Carvalho Chehab pr_err("usb_register failed, error = %d\n", ret); 5330c0d06caSMauro Carvalho Chehab 5340c0d06caSMauro Carvalho Chehab return ret; 5350c0d06caSMauro Carvalho Chehab } 5360c0d06caSMauro Carvalho Chehab 5370c0d06caSMauro Carvalho Chehab static void __exit au0828_exit(void) 5380c0d06caSMauro Carvalho Chehab { 5390c0d06caSMauro Carvalho Chehab usb_deregister(&au0828_usb_driver); 5400c0d06caSMauro Carvalho Chehab } 5410c0d06caSMauro Carvalho Chehab 5420c0d06caSMauro Carvalho Chehab module_init(au0828_init); 5430c0d06caSMauro Carvalho Chehab module_exit(au0828_exit); 5440c0d06caSMauro Carvalho Chehab 5450c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products"); 5460c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); 5470c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 5482fcfd317SMauro Carvalho Chehab MODULE_VERSION("0.0.3"); 549