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);
1399832e155SJavier Martinez Canillas 		media_device_cleanup(dev->media_dev);
140bed69196SRafael Lourenço de Lima Chehab 		kfree(dev->media_dev);
141bed69196SRafael Lourenço de Lima Chehab 		dev->media_dev = NULL;
142bed69196SRafael Lourenço de Lima Chehab 	}
143bed69196SRafael Lourenço de Lima Chehab #endif
144bed69196SRafael Lourenço de Lima Chehab }
145bed69196SRafael Lourenço de Lima Chehab 
146823beb7eSHans Verkuil static void au0828_usb_release(struct au0828_dev *dev)
147823beb7eSHans Verkuil {
148bed69196SRafael Lourenço de Lima Chehab 	au0828_unregister_media_device(dev);
149bed69196SRafael Lourenço de Lima Chehab 
150823beb7eSHans Verkuil 	/* I2C */
151823beb7eSHans Verkuil 	au0828_i2c_unregister(dev);
152823beb7eSHans Verkuil 
153823beb7eSHans Verkuil 	kfree(dev);
154823beb7eSHans Verkuil }
155823beb7eSHans Verkuil 
156823beb7eSHans Verkuil #ifdef CONFIG_VIDEO_AU0828_V4L2
157d1f33737SMauro Carvalho Chehab 
158d1f33737SMauro Carvalho Chehab static void au0828_usb_v4l2_media_release(struct au0828_dev *dev)
159d1f33737SMauro Carvalho Chehab {
160d1f33737SMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
161d1f33737SMauro Carvalho Chehab 	int i;
162d1f33737SMauro Carvalho Chehab 
163d1f33737SMauro Carvalho Chehab 	for (i = 0; i < AU0828_MAX_INPUT; i++) {
164d1f33737SMauro Carvalho Chehab 		if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED)
165d1f33737SMauro Carvalho Chehab 			return;
166d1f33737SMauro Carvalho Chehab 		media_device_unregister_entity(&dev->input_ent[i]);
167d1f33737SMauro Carvalho Chehab 	}
168d1f33737SMauro Carvalho Chehab #endif
169d1f33737SMauro Carvalho Chehab }
170d1f33737SMauro Carvalho Chehab 
171823beb7eSHans Verkuil static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev)
172823beb7eSHans Verkuil {
173823beb7eSHans Verkuil 	struct au0828_dev *dev =
174823beb7eSHans Verkuil 		container_of(v4l2_dev, struct au0828_dev, v4l2_dev);
175823beb7eSHans Verkuil 
176823beb7eSHans Verkuil 	v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl);
177823beb7eSHans Verkuil 	v4l2_device_unregister(&dev->v4l2_dev);
1784e26f3abSMauro Carvalho Chehab 	au0828_usb_v4l2_media_release(dev);
179823beb7eSHans Verkuil 	au0828_usb_release(dev);
180823beb7eSHans Verkuil }
181823beb7eSHans Verkuil #endif
182823beb7eSHans Verkuil 
1830c0d06caSMauro Carvalho Chehab static void au0828_usb_disconnect(struct usb_interface *interface)
1840c0d06caSMauro Carvalho Chehab {
1850c0d06caSMauro Carvalho Chehab 	struct au0828_dev *dev = usb_get_intfdata(interface);
1860c0d06caSMauro Carvalho Chehab 
1870c0d06caSMauro Carvalho Chehab 	dprintk(1, "%s()\n", __func__);
1880c0d06caSMauro Carvalho Chehab 
189eb336eabSShuah Khan 	/* there is a small window after disconnect, before
190eb336eabSShuah Khan 	   dev->usbdev is NULL, for poll (e.g: IR) try to access
191eb336eabSShuah Khan 	   the device and fill the dmesg with error messages.
192eb336eabSShuah Khan 	   Set the status so poll routines can check and avoid
193eb336eabSShuah Khan 	   access after disconnect.
194eb336eabSShuah Khan 	*/
195eb336eabSShuah Khan 	dev->dev_state = DEV_DISCONNECTED;
196eb336eabSShuah Khan 
1972fcfd317SMauro Carvalho Chehab 	au0828_rc_unregister(dev);
1980c0d06caSMauro Carvalho Chehab 	/* Digital TV */
1990c0d06caSMauro Carvalho Chehab 	au0828_dvb_unregister(dev);
2000c0d06caSMauro Carvalho Chehab 
2010c0d06caSMauro Carvalho Chehab 	usb_set_intfdata(interface, NULL);
2020c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->mutex);
2030c0d06caSMauro Carvalho Chehab 	dev->usbdev = NULL;
2040c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->mutex);
205823beb7eSHans Verkuil #ifdef CONFIG_VIDEO_AU0828_V4L2
206823beb7eSHans Verkuil 	if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) {
207823beb7eSHans Verkuil 		au0828_analog_unregister(dev);
208823beb7eSHans Verkuil 		v4l2_device_disconnect(&dev->v4l2_dev);
209823beb7eSHans Verkuil 		v4l2_device_put(&dev->v4l2_dev);
2107e9a8ad5SMauro Carvalho Chehab 		/*
2117e9a8ad5SMauro Carvalho Chehab 		 * No need to call au0828_usb_release() if V4L2 is enabled,
2127e9a8ad5SMauro Carvalho Chehab 		 * as this is already called via au0828_usb_v4l2_release()
2137e9a8ad5SMauro Carvalho Chehab 		 */
214823beb7eSHans Verkuil 		return;
215823beb7eSHans Verkuil 	}
216823beb7eSHans Verkuil #endif
217823beb7eSHans Verkuil 	au0828_usb_release(dev);
2180c0d06caSMauro Carvalho Chehab }
2190c0d06caSMauro Carvalho Chehab 
2209f806795SMauro Carvalho Chehab static int au0828_media_device_init(struct au0828_dev *dev,
221bed69196SRafael Lourenço de Lima Chehab 				    struct usb_device *udev)
222bed69196SRafael Lourenço de Lima Chehab {
223bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER
224bed69196SRafael Lourenço de Lima Chehab 	struct media_device *mdev;
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)
2289f806795SMauro Carvalho Chehab 		return -ENOMEM;
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 
2429832e155SJavier Martinez Canillas 	media_device_init(mdev);
243bed69196SRafael Lourenço de Lima Chehab 
244bed69196SRafael Lourenço de Lima Chehab 	dev->media_dev = mdev;
245bed69196SRafael Lourenço de Lima Chehab #endif
2469f806795SMauro Carvalho Chehab 	return 0;
247bed69196SRafael Lourenço de Lima Chehab }
248bed69196SRafael Lourenço de Lima Chehab 
249bed69196SRafael Lourenço de Lima Chehab 
2504e26f3abSMauro Carvalho Chehab static int au0828_create_media_graph(struct au0828_dev *dev)
251bed69196SRafael Lourenço de Lima Chehab {
252bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER
253bed69196SRafael Lourenço de Lima Chehab 	struct media_device *mdev = dev->media_dev;
254bed69196SRafael Lourenço de Lima Chehab 	struct media_entity *entity;
255bed69196SRafael Lourenço de Lima Chehab 	struct media_entity *tuner = NULL, *decoder = NULL;
2564e26f3abSMauro Carvalho Chehab 	int i, ret;
257bed69196SRafael Lourenço de Lima Chehab 
258bed69196SRafael Lourenço de Lima Chehab 	if (!mdev)
2594e26f3abSMauro Carvalho Chehab 		return 0;
260bed69196SRafael Lourenço de Lima Chehab 
261bed69196SRafael Lourenço de Lima Chehab 	media_device_for_each_entity(entity, mdev) {
2620e576b76SMauro Carvalho Chehab 		switch (entity->function) {
2634ca72efaSMauro Carvalho Chehab 		case MEDIA_ENT_F_TUNER:
264bed69196SRafael Lourenço de Lima Chehab 			tuner = entity;
265bed69196SRafael Lourenço de Lima Chehab 			break;
2664ca72efaSMauro Carvalho Chehab 		case MEDIA_ENT_F_ATV_DECODER:
267bed69196SRafael Lourenço de Lima Chehab 			decoder = entity;
268bed69196SRafael Lourenço de Lima Chehab 			break;
269bed69196SRafael Lourenço de Lima Chehab 		}
270bed69196SRafael Lourenço de Lima Chehab 	}
271bed69196SRafael Lourenço de Lima Chehab 
272bed69196SRafael Lourenço de Lima Chehab 	/* Analog setup, using tuner as a link */
273bed69196SRafael Lourenço de Lima Chehab 
27428b6ba11SMauro Carvalho Chehab 	/* Something bad happened! */
275bed69196SRafael Lourenço de Lima Chehab 	if (!decoder)
2764e26f3abSMauro Carvalho Chehab 		return -EINVAL;
277bed69196SRafael Lourenço de Lima Chehab 
2784e26f3abSMauro Carvalho Chehab 	if (tuner) {
2794e26f3abSMauro Carvalho Chehab 		ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT,
2804e26f3abSMauro Carvalho Chehab 					    decoder, 0,
281bed69196SRafael Lourenço de Lima Chehab 					    MEDIA_LNK_FL_ENABLED);
2824e26f3abSMauro Carvalho Chehab 		if (ret)
2834e26f3abSMauro Carvalho Chehab 			return ret;
2844e26f3abSMauro Carvalho Chehab 	}
2854e26f3abSMauro Carvalho Chehab 	ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0,
286bed69196SRafael Lourenço de Lima Chehab 				    MEDIA_LNK_FL_ENABLED);
2874e26f3abSMauro Carvalho Chehab 	if (ret)
2884e26f3abSMauro Carvalho Chehab 		return ret;
2894e26f3abSMauro Carvalho Chehab 	ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0,
290bed69196SRafael Lourenço de Lima Chehab 				    MEDIA_LNK_FL_ENABLED);
2914e26f3abSMauro Carvalho Chehab 	if (ret)
2924e26f3abSMauro Carvalho Chehab 		return ret;
29328b6ba11SMauro Carvalho Chehab 
29428b6ba11SMauro Carvalho Chehab 	for (i = 0; i < AU0828_MAX_INPUT; i++) {
29528b6ba11SMauro Carvalho Chehab 		struct media_entity *ent = &dev->input_ent[i];
29628b6ba11SMauro Carvalho Chehab 
29728b6ba11SMauro Carvalho Chehab 		if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED)
29828b6ba11SMauro Carvalho Chehab 			break;
29928b6ba11SMauro Carvalho Chehab 
30028b6ba11SMauro Carvalho Chehab 		switch (AUVI_INPUT(i).type) {
30128b6ba11SMauro Carvalho Chehab 		case AU0828_VMUX_CABLE:
30228b6ba11SMauro Carvalho Chehab 		case AU0828_VMUX_TELEVISION:
30328b6ba11SMauro Carvalho Chehab 		case AU0828_VMUX_DVB:
3044e26f3abSMauro Carvalho Chehab 			if (!tuner)
3054e26f3abSMauro Carvalho Chehab 				break;
3064e26f3abSMauro Carvalho Chehab 
3074e26f3abSMauro Carvalho Chehab 			ret = media_create_pad_link(ent, 0, tuner,
30828b6ba11SMauro Carvalho Chehab 						    TUNER_PAD_RF_INPUT,
30928b6ba11SMauro Carvalho Chehab 						    MEDIA_LNK_FL_ENABLED);
3104e26f3abSMauro Carvalho Chehab 			if (ret)
3114e26f3abSMauro Carvalho Chehab 				return ret;
31228b6ba11SMauro Carvalho Chehab 			break;
31328b6ba11SMauro Carvalho Chehab 		case AU0828_VMUX_COMPOSITE:
31428b6ba11SMauro Carvalho Chehab 		case AU0828_VMUX_SVIDEO:
31528b6ba11SMauro Carvalho Chehab 		default: /* AU0828_VMUX_DEBUG */
31628b6ba11SMauro Carvalho Chehab 			/* FIXME: fix the decoder PAD */
3174e26f3abSMauro Carvalho Chehab 			ret = media_create_pad_link(ent, 0, decoder, 0, 0);
3184e26f3abSMauro Carvalho Chehab 			if (ret)
3194e26f3abSMauro Carvalho Chehab 				return ret;
32028b6ba11SMauro Carvalho Chehab 			break;
32128b6ba11SMauro Carvalho Chehab 		}
32228b6ba11SMauro Carvalho Chehab 	}
323bed69196SRafael Lourenço de Lima Chehab #endif
3244e26f3abSMauro Carvalho Chehab 	return 0;
325bed69196SRafael Lourenço de Lima Chehab }
326bed69196SRafael Lourenço de Lima Chehab 
3270c0d06caSMauro Carvalho Chehab static int au0828_usb_probe(struct usb_interface *interface,
3280c0d06caSMauro Carvalho Chehab 	const struct usb_device_id *id)
3290c0d06caSMauro Carvalho Chehab {
3308a4e7866SMichael Krufky 	int ifnum;
331f251b3e7STim Mester 	int retval = 0;
332f251b3e7STim Mester 
3330c0d06caSMauro Carvalho Chehab 	struct au0828_dev *dev;
3340c0d06caSMauro Carvalho Chehab 	struct usb_device *usbdev = interface_to_usbdev(interface);
3350c0d06caSMauro Carvalho Chehab 
3360c0d06caSMauro Carvalho Chehab 	ifnum = interface->altsetting->desc.bInterfaceNumber;
3370c0d06caSMauro Carvalho Chehab 
3380c0d06caSMauro Carvalho Chehab 	if (ifnum != 0)
3390c0d06caSMauro Carvalho Chehab 		return -ENODEV;
3400c0d06caSMauro Carvalho Chehab 
3410c0d06caSMauro Carvalho Chehab 	dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__,
3420c0d06caSMauro Carvalho Chehab 		le16_to_cpu(usbdev->descriptor.idVendor),
3430c0d06caSMauro Carvalho Chehab 		le16_to_cpu(usbdev->descriptor.idProduct),
3440c0d06caSMauro Carvalho Chehab 		ifnum);
3450c0d06caSMauro Carvalho Chehab 
3460c0d06caSMauro Carvalho Chehab 	/*
3470c0d06caSMauro Carvalho Chehab 	 * Make sure we have 480 Mbps of bandwidth, otherwise things like
3480c0d06caSMauro Carvalho Chehab 	 * video stream wouldn't likely work, since 12 Mbps is generally
3490c0d06caSMauro Carvalho Chehab 	 * not enough even for most Digital TV streams.
3500c0d06caSMauro Carvalho Chehab 	 */
3510c0d06caSMauro Carvalho Chehab 	if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
35283afb32aSMauro Carvalho Chehab 		pr_err("au0828: Device initialization failed.\n");
35383afb32aSMauro Carvalho Chehab 		pr_err("au0828: Device must be connected to a high-speed USB 2.0 port.\n");
3540c0d06caSMauro Carvalho Chehab 		return -ENODEV;
3550c0d06caSMauro Carvalho Chehab 	}
3560c0d06caSMauro Carvalho Chehab 
3570c0d06caSMauro Carvalho Chehab 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
3580c0d06caSMauro Carvalho Chehab 	if (dev == NULL) {
35983afb32aSMauro Carvalho Chehab 		pr_err("%s() Unable to allocate memory\n", __func__);
3600c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
3610c0d06caSMauro Carvalho Chehab 	}
3620c0d06caSMauro Carvalho Chehab 
3630c0d06caSMauro Carvalho Chehab 	mutex_init(&dev->lock);
3640c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
3650c0d06caSMauro Carvalho Chehab 	mutex_init(&dev->mutex);
3660c0d06caSMauro Carvalho Chehab 	mutex_init(&dev->dvb.lock);
3670c0d06caSMauro Carvalho Chehab 	dev->usbdev = usbdev;
3680c0d06caSMauro Carvalho Chehab 	dev->boardnr = id->driver_info;
369e42c8c6eSRafael Lourenço de Lima Chehab 	dev->board = au0828_boards[dev->boardnr];
370e42c8c6eSRafael Lourenço de Lima Chehab 
3719832e155SJavier Martinez Canillas 	/* Initialize the media controller */
3729f806795SMauro Carvalho Chehab 	retval = au0828_media_device_init(dev, usbdev);
3739f806795SMauro Carvalho Chehab 	if (retval) {
3749f806795SMauro Carvalho Chehab 		pr_err("%s() au0828_media_device_init failed\n",
3759f806795SMauro Carvalho Chehab 		       __func__);
3769f806795SMauro Carvalho Chehab 		mutex_unlock(&dev->lock);
3779f806795SMauro Carvalho Chehab 		kfree(dev);
3789f806795SMauro Carvalho Chehab 		return retval;
3799f806795SMauro Carvalho Chehab 	}
3800c0d06caSMauro Carvalho Chehab 
3818a4e7866SMichael Krufky #ifdef CONFIG_VIDEO_AU0828_V4L2
382823beb7eSHans Verkuil 	dev->v4l2_dev.release = au0828_usb_v4l2_release;
383823beb7eSHans Verkuil 
3840c0d06caSMauro Carvalho Chehab 	/* Create the v4l2_device */
385bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER
386bed69196SRafael Lourenço de Lima Chehab 	dev->v4l2_dev.mdev = dev->media_dev;
387bed69196SRafael Lourenço de Lima Chehab #endif
3880c0d06caSMauro Carvalho Chehab 	retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
3890c0d06caSMauro Carvalho Chehab 	if (retval) {
390e8c26f45SHans Verkuil 		pr_err("%s() v4l2_device_register failed\n",
3910c0d06caSMauro Carvalho Chehab 		       __func__);
3920c0d06caSMauro Carvalho Chehab 		mutex_unlock(&dev->lock);
3930c0d06caSMauro Carvalho Chehab 		kfree(dev);
394e8c26f45SHans Verkuil 		return retval;
3950c0d06caSMauro Carvalho Chehab 	}
396e8c26f45SHans Verkuil 	/* This control handler will inherit the controls from au8522 */
397e8c26f45SHans Verkuil 	retval = v4l2_ctrl_handler_init(&dev->v4l2_ctrl_hdl, 4);
398e8c26f45SHans Verkuil 	if (retval) {
399e8c26f45SHans Verkuil 		pr_err("%s() v4l2_ctrl_handler_init failed\n",
400e8c26f45SHans Verkuil 		       __func__);
401e8c26f45SHans Verkuil 		mutex_unlock(&dev->lock);
402e8c26f45SHans Verkuil 		kfree(dev);
403e8c26f45SHans Verkuil 		return retval;
404e8c26f45SHans Verkuil 	}
405e8c26f45SHans Verkuil 	dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl;
4068a4e7866SMichael Krufky #endif
4070c0d06caSMauro Carvalho Chehab 
4080c0d06caSMauro Carvalho Chehab 	/* Power Up the bridge */
4090c0d06caSMauro Carvalho Chehab 	au0828_write(dev, REG_600, 1 << 4);
4100c0d06caSMauro Carvalho Chehab 
4110c0d06caSMauro Carvalho Chehab 	/* Bring up the GPIO's and supporting devices */
4120c0d06caSMauro Carvalho Chehab 	au0828_gpio_setup(dev);
4130c0d06caSMauro Carvalho Chehab 
4140c0d06caSMauro Carvalho Chehab 	/* I2C */
4150c0d06caSMauro Carvalho Chehab 	au0828_i2c_register(dev);
4160c0d06caSMauro Carvalho Chehab 
4170c0d06caSMauro Carvalho Chehab 	/* Setup */
4180c0d06caSMauro Carvalho Chehab 	au0828_card_setup(dev);
4190c0d06caSMauro Carvalho Chehab 
4208a4e7866SMichael Krufky #ifdef CONFIG_VIDEO_AU0828_V4L2
4210c0d06caSMauro Carvalho Chehab 	/* Analog TV */
4220c0d06caSMauro Carvalho Chehab 	if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
4230c0d06caSMauro Carvalho Chehab 		au0828_analog_register(dev, interface);
4248a4e7866SMichael Krufky #endif
4250c0d06caSMauro Carvalho Chehab 
4260c0d06caSMauro Carvalho Chehab 	/* Digital TV */
427f251b3e7STim Mester 	retval = au0828_dvb_register(dev);
428f251b3e7STim Mester 	if (retval)
429f251b3e7STim Mester 		pr_err("%s() au0282_dev_register failed\n",
430f251b3e7STim Mester 		       __func__);
431f251b3e7STim Mester 
4322fcfd317SMauro Carvalho Chehab 	/* Remote controller */
4332fcfd317SMauro Carvalho Chehab 	au0828_rc_register(dev);
4340c0d06caSMauro Carvalho Chehab 
4352fcfd317SMauro Carvalho Chehab 	/*
4362fcfd317SMauro Carvalho Chehab 	 * Store the pointer to the au0828_dev so it can be accessed in
4372fcfd317SMauro Carvalho Chehab 	 * au0828_usb_disconnect
4382fcfd317SMauro Carvalho Chehab 	 */
4390c0d06caSMauro Carvalho Chehab 	usb_set_intfdata(interface, dev);
4400c0d06caSMauro Carvalho Chehab 
44183afb32aSMauro Carvalho Chehab 	pr_info("Registered device AU0828 [%s]\n",
4420c0d06caSMauro Carvalho Chehab 		dev->board.name == NULL ? "Unset" : dev->board.name);
4430c0d06caSMauro Carvalho Chehab 
4440c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
4450c0d06caSMauro Carvalho Chehab 
4464e26f3abSMauro Carvalho Chehab 	retval = au0828_create_media_graph(dev);
4474e26f3abSMauro Carvalho Chehab 	if (retval) {
4484e26f3abSMauro Carvalho Chehab 		pr_err("%s() au0282_dev_register failed to create graph\n",
4494e26f3abSMauro Carvalho Chehab 		       __func__);
4509832e155SJavier Martinez Canillas 		goto done;
4514e26f3abSMauro Carvalho Chehab 	}
452bed69196SRafael Lourenço de Lima Chehab 
4539832e155SJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER
4549832e155SJavier Martinez Canillas 	retval = media_device_register(dev->media_dev);
4559832e155SJavier Martinez Canillas #endif
4569832e155SJavier Martinez Canillas 
4579832e155SJavier Martinez Canillas done:
4589832e155SJavier Martinez Canillas 	if (retval < 0)
4599832e155SJavier Martinez Canillas 		au0828_usb_disconnect(interface);
4609832e155SJavier Martinez Canillas 
461f251b3e7STim Mester 	return retval;
4620c0d06caSMauro Carvalho Chehab }
4630c0d06caSMauro Carvalho Chehab 
464aaeac199SMauro Carvalho Chehab static int au0828_suspend(struct usb_interface *interface,
465aaeac199SMauro Carvalho Chehab 				pm_message_t message)
466aaeac199SMauro Carvalho Chehab {
467aaeac199SMauro Carvalho Chehab 	struct au0828_dev *dev = usb_get_intfdata(interface);
468aaeac199SMauro Carvalho Chehab 
469aaeac199SMauro Carvalho Chehab 	if (!dev)
470aaeac199SMauro Carvalho Chehab 		return 0;
471aaeac199SMauro Carvalho Chehab 
47281187240SMauro Carvalho Chehab 	pr_info("Suspend\n");
47381187240SMauro Carvalho Chehab 
474aaeac199SMauro Carvalho Chehab 	au0828_rc_suspend(dev);
4751a1ba95eSMauro Carvalho Chehab 	au0828_v4l2_suspend(dev);
476b799de75SMauro Carvalho Chehab 	au0828_dvb_suspend(dev);
477aaeac199SMauro Carvalho Chehab 
478aaeac199SMauro Carvalho Chehab 	/* FIXME: should suspend also ATV/DTV */
479aaeac199SMauro Carvalho Chehab 
480aaeac199SMauro Carvalho Chehab 	return 0;
481aaeac199SMauro Carvalho Chehab }
482aaeac199SMauro Carvalho Chehab 
483aaeac199SMauro Carvalho Chehab static int au0828_resume(struct usb_interface *interface)
484aaeac199SMauro Carvalho Chehab {
485aaeac199SMauro Carvalho Chehab 	struct au0828_dev *dev = usb_get_intfdata(interface);
486aaeac199SMauro Carvalho Chehab 	if (!dev)
487aaeac199SMauro Carvalho Chehab 		return 0;
488aaeac199SMauro Carvalho Chehab 
48981187240SMauro Carvalho Chehab 	pr_info("Resume\n");
49081187240SMauro Carvalho Chehab 
491fa500461SMauro Carvalho Chehab 	/* Power Up the bridge */
492fa500461SMauro Carvalho Chehab 	au0828_write(dev, REG_600, 1 << 4);
493fa500461SMauro Carvalho Chehab 
494fa500461SMauro Carvalho Chehab 	/* Bring up the GPIO's and supporting devices */
495fa500461SMauro Carvalho Chehab 	au0828_gpio_setup(dev);
496fa500461SMauro Carvalho Chehab 
497aaeac199SMauro Carvalho Chehab 	au0828_rc_resume(dev);
4981a1ba95eSMauro Carvalho Chehab 	au0828_v4l2_resume(dev);
499b799de75SMauro Carvalho Chehab 	au0828_dvb_resume(dev);
500aaeac199SMauro Carvalho Chehab 
501aaeac199SMauro Carvalho Chehab 	/* FIXME: should resume also ATV/DTV */
502aaeac199SMauro Carvalho Chehab 
503aaeac199SMauro Carvalho Chehab 	return 0;
504aaeac199SMauro Carvalho Chehab }
505aaeac199SMauro Carvalho Chehab 
5060c0d06caSMauro Carvalho Chehab static struct usb_driver au0828_usb_driver = {
50783afb32aSMauro Carvalho Chehab 	.name		= KBUILD_MODNAME,
5080c0d06caSMauro Carvalho Chehab 	.probe		= au0828_usb_probe,
5090c0d06caSMauro Carvalho Chehab 	.disconnect	= au0828_usb_disconnect,
5100c0d06caSMauro Carvalho Chehab 	.id_table	= au0828_usb_id_table,
511aaeac199SMauro Carvalho Chehab 	.suspend	= au0828_suspend,
512aaeac199SMauro Carvalho Chehab 	.resume		= au0828_resume,
513aaeac199SMauro Carvalho Chehab 	.reset_resume	= au0828_resume,
5140c0d06caSMauro Carvalho Chehab };
5150c0d06caSMauro Carvalho Chehab 
5160c0d06caSMauro Carvalho Chehab static int __init au0828_init(void)
5170c0d06caSMauro Carvalho Chehab {
5180c0d06caSMauro Carvalho Chehab 	int ret;
5190c0d06caSMauro Carvalho Chehab 
5200c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 1)
52183afb32aSMauro Carvalho Chehab 		pr_info("%s() Debugging is enabled\n", __func__);
5220c0d06caSMauro Carvalho Chehab 
5230c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 2)
52483afb32aSMauro Carvalho Chehab 		pr_info("%s() USB Debugging is enabled\n", __func__);
5250c0d06caSMauro Carvalho Chehab 
5260c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 4)
52783afb32aSMauro Carvalho Chehab 		pr_info("%s() I2C Debugging is enabled\n", __func__);
5280c0d06caSMauro Carvalho Chehab 
5290c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 8)
53083afb32aSMauro Carvalho Chehab 		pr_info("%s() Bridge Debugging is enabled\n",
5310c0d06caSMauro Carvalho Chehab 		       __func__);
5320c0d06caSMauro Carvalho Chehab 
5332fcfd317SMauro Carvalho Chehab 	if (au0828_debug & 16)
53483afb32aSMauro Carvalho Chehab 		pr_info("%s() IR Debugging is enabled\n",
5352fcfd317SMauro Carvalho Chehab 		       __func__);
5362fcfd317SMauro Carvalho Chehab 
53783afb32aSMauro Carvalho Chehab 	pr_info("au0828 driver loaded\n");
5380c0d06caSMauro Carvalho Chehab 
5390c0d06caSMauro Carvalho Chehab 	ret = usb_register(&au0828_usb_driver);
5400c0d06caSMauro Carvalho Chehab 	if (ret)
54183afb32aSMauro Carvalho Chehab 		pr_err("usb_register failed, error = %d\n", ret);
5420c0d06caSMauro Carvalho Chehab 
5430c0d06caSMauro Carvalho Chehab 	return ret;
5440c0d06caSMauro Carvalho Chehab }
5450c0d06caSMauro Carvalho Chehab 
5460c0d06caSMauro Carvalho Chehab static void __exit au0828_exit(void)
5470c0d06caSMauro Carvalho Chehab {
5480c0d06caSMauro Carvalho Chehab 	usb_deregister(&au0828_usb_driver);
5490c0d06caSMauro Carvalho Chehab }
5500c0d06caSMauro Carvalho Chehab 
5510c0d06caSMauro Carvalho Chehab module_init(au0828_init);
5520c0d06caSMauro Carvalho Chehab module_exit(au0828_exit);
5530c0d06caSMauro Carvalho Chehab 
5540c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products");
5550c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
5560c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
5572fcfd317SMauro Carvalho Chehab MODULE_VERSION("0.0.3");
558