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"
23f90c5d79SShuah Khan #include "au8522.h"
2483afb32aSMauro Carvalho Chehab 
250c0d06caSMauro Carvalho Chehab #include <linux/module.h>
260c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
270c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h>
280c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
290c0d06caSMauro Carvalho Chehab #include <linux/mutex.h>
300c0d06caSMauro Carvalho Chehab 
31188d2d55SMauro Carvalho Chehab /* Due to enum tuner_pad_index */
32188d2d55SMauro Carvalho Chehab #include <media/tuner.h>
33188d2d55SMauro Carvalho Chehab 
340c0d06caSMauro Carvalho Chehab /*
350c0d06caSMauro Carvalho Chehab  * 1 = General debug messages
360c0d06caSMauro Carvalho Chehab  * 2 = USB handling
370c0d06caSMauro Carvalho Chehab  * 4 = I2C related
380c0d06caSMauro Carvalho Chehab  * 8 = Bridge related
392fcfd317SMauro Carvalho Chehab  * 16 = IR related
400c0d06caSMauro Carvalho Chehab  */
410c0d06caSMauro Carvalho Chehab int au0828_debug;
420c0d06caSMauro Carvalho Chehab module_param_named(debug, au0828_debug, int, 0644);
432fcfd317SMauro Carvalho Chehab MODULE_PARM_DESC(debug,
442fcfd317SMauro Carvalho Chehab 		 "set debug bitmask: 1=general, 2=USB, 4=I2C, 8=bridge, 16=IR");
450c0d06caSMauro Carvalho Chehab 
460c0d06caSMauro Carvalho Chehab static unsigned int disable_usb_speed_check;
470c0d06caSMauro Carvalho Chehab module_param(disable_usb_speed_check, int, 0444);
480c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(disable_usb_speed_check,
490c0d06caSMauro Carvalho Chehab 		 "override min bandwidth requirement of 480M bps");
500c0d06caSMauro Carvalho Chehab 
510c0d06caSMauro Carvalho Chehab #define _AU0828_BULKPIPE 0x03
520c0d06caSMauro Carvalho Chehab #define _BULKPIPESIZE 0xffff
530c0d06caSMauro Carvalho Chehab 
540c0d06caSMauro Carvalho Chehab static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
550c0d06caSMauro Carvalho Chehab 			    u16 index);
560c0d06caSMauro Carvalho Chehab static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
570c0d06caSMauro Carvalho Chehab 	u16 index, unsigned char *cp, u16 size);
580c0d06caSMauro Carvalho Chehab 
590c0d06caSMauro Carvalho Chehab /* USB Direction */
600c0d06caSMauro Carvalho Chehab #define CMD_REQUEST_IN		0x00
610c0d06caSMauro Carvalho Chehab #define CMD_REQUEST_OUT		0x01
620c0d06caSMauro Carvalho Chehab 
630c0d06caSMauro Carvalho Chehab u32 au0828_readreg(struct au0828_dev *dev, u16 reg)
640c0d06caSMauro Carvalho Chehab {
650c0d06caSMauro Carvalho Chehab 	u8 result = 0;
660c0d06caSMauro Carvalho Chehab 
670c0d06caSMauro Carvalho Chehab 	recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, &result, 1);
680c0d06caSMauro Carvalho Chehab 	dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, result);
690c0d06caSMauro Carvalho Chehab 
700c0d06caSMauro Carvalho Chehab 	return result;
710c0d06caSMauro Carvalho Chehab }
720c0d06caSMauro Carvalho Chehab 
730c0d06caSMauro Carvalho Chehab u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val)
740c0d06caSMauro Carvalho Chehab {
750c0d06caSMauro Carvalho Chehab 	dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
760c0d06caSMauro Carvalho Chehab 	return send_control_msg(dev, CMD_REQUEST_OUT, val, reg);
770c0d06caSMauro Carvalho Chehab }
780c0d06caSMauro Carvalho Chehab 
790c0d06caSMauro Carvalho Chehab static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
800c0d06caSMauro Carvalho Chehab 	u16 index)
810c0d06caSMauro Carvalho Chehab {
820c0d06caSMauro Carvalho Chehab 	int status = -ENODEV;
830c0d06caSMauro Carvalho Chehab 
840c0d06caSMauro Carvalho Chehab 	if (dev->usbdev) {
850c0d06caSMauro Carvalho Chehab 
860c0d06caSMauro Carvalho Chehab 		/* cp must be memory that has been allocated by kmalloc */
870c0d06caSMauro Carvalho Chehab 		status = usb_control_msg(dev->usbdev,
880c0d06caSMauro Carvalho Chehab 				usb_sndctrlpipe(dev->usbdev, 0),
890c0d06caSMauro Carvalho Chehab 				request,
900c0d06caSMauro Carvalho Chehab 				USB_DIR_OUT | USB_TYPE_VENDOR |
910c0d06caSMauro Carvalho Chehab 					USB_RECIP_DEVICE,
920c0d06caSMauro Carvalho Chehab 				value, index, NULL, 0, 1000);
930c0d06caSMauro Carvalho Chehab 
940c0d06caSMauro Carvalho Chehab 		status = min(status, 0);
950c0d06caSMauro Carvalho Chehab 
960c0d06caSMauro Carvalho Chehab 		if (status < 0) {
9783afb32aSMauro Carvalho Chehab 			pr_err("%s() Failed sending control message, error %d.\n",
980c0d06caSMauro Carvalho Chehab 				__func__, status);
990c0d06caSMauro Carvalho Chehab 		}
1000c0d06caSMauro Carvalho Chehab 
1010c0d06caSMauro Carvalho Chehab 	}
1020c0d06caSMauro Carvalho Chehab 
1030c0d06caSMauro Carvalho Chehab 	return status;
1040c0d06caSMauro Carvalho Chehab }
1050c0d06caSMauro Carvalho Chehab 
1060c0d06caSMauro Carvalho Chehab static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
1070c0d06caSMauro Carvalho Chehab 	u16 index, unsigned char *cp, u16 size)
1080c0d06caSMauro Carvalho Chehab {
1090c0d06caSMauro Carvalho Chehab 	int status = -ENODEV;
1100c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->mutex);
1110c0d06caSMauro Carvalho Chehab 	if (dev->usbdev) {
1120c0d06caSMauro Carvalho Chehab 		status = usb_control_msg(dev->usbdev,
1130c0d06caSMauro Carvalho Chehab 				usb_rcvctrlpipe(dev->usbdev, 0),
1140c0d06caSMauro Carvalho Chehab 				request,
1150c0d06caSMauro Carvalho Chehab 				USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1160c0d06caSMauro Carvalho Chehab 				value, index,
1170c0d06caSMauro Carvalho Chehab 				dev->ctrlmsg, size, 1000);
1180c0d06caSMauro Carvalho Chehab 
1190c0d06caSMauro Carvalho Chehab 		status = min(status, 0);
1200c0d06caSMauro Carvalho Chehab 
1210c0d06caSMauro Carvalho Chehab 		if (status < 0) {
12283afb32aSMauro Carvalho Chehab 			pr_err("%s() Failed receiving control message, error %d.\n",
1230c0d06caSMauro Carvalho Chehab 				__func__, status);
1240c0d06caSMauro Carvalho Chehab 		}
1250c0d06caSMauro Carvalho Chehab 
1260c0d06caSMauro Carvalho Chehab 		/* the host controller requires heap allocated memory, which
1270c0d06caSMauro Carvalho Chehab 		   is why we didn't just pass "cp" into usb_control_msg */
1280c0d06caSMauro Carvalho Chehab 		memcpy(cp, dev->ctrlmsg, size);
1290c0d06caSMauro Carvalho Chehab 	}
1300c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->mutex);
1310c0d06caSMauro Carvalho Chehab 	return status;
1320c0d06caSMauro Carvalho Chehab }
1330c0d06caSMauro Carvalho Chehab 
134bed69196SRafael Lourenço de Lima Chehab static void au0828_unregister_media_device(struct au0828_dev *dev)
135bed69196SRafael Lourenço de Lima Chehab {
136bed69196SRafael Lourenço de Lima Chehab 
137bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER
138182dde7cSShuah Khan 	if (dev->media_dev &&
139182dde7cSShuah Khan 		media_devnode_is_registered(&dev->media_dev->devnode)) {
140bed69196SRafael Lourenço de Lima Chehab 		media_device_unregister(dev->media_dev);
1419832e155SJavier Martinez Canillas 		media_device_cleanup(dev->media_dev);
142bed69196SRafael Lourenço de Lima Chehab 		dev->media_dev = NULL;
143bed69196SRafael Lourenço de Lima Chehab 	}
144bed69196SRafael Lourenço de Lima Chehab #endif
145bed69196SRafael Lourenço de Lima Chehab }
146bed69196SRafael Lourenço de Lima Chehab 
1477b606ffdSMauro Carvalho Chehab void au0828_usb_release(struct au0828_dev *dev)
148823beb7eSHans Verkuil {
149bed69196SRafael Lourenço de Lima Chehab 	au0828_unregister_media_device(dev);
150bed69196SRafael Lourenço de Lima Chehab 
151823beb7eSHans Verkuil 	/* I2C */
152823beb7eSHans Verkuil 	au0828_i2c_unregister(dev);
153823beb7eSHans Verkuil 
154823beb7eSHans Verkuil 	kfree(dev);
155823beb7eSHans Verkuil }
156823beb7eSHans Verkuil 
1570c0d06caSMauro Carvalho Chehab static void au0828_usb_disconnect(struct usb_interface *interface)
1580c0d06caSMauro Carvalho Chehab {
1590c0d06caSMauro Carvalho Chehab 	struct au0828_dev *dev = usb_get_intfdata(interface);
1600c0d06caSMauro Carvalho Chehab 
1610c0d06caSMauro Carvalho Chehab 	dprintk(1, "%s()\n", __func__);
1620c0d06caSMauro Carvalho Chehab 
163eb336eabSShuah Khan 	/* there is a small window after disconnect, before
164eb336eabSShuah Khan 	   dev->usbdev is NULL, for poll (e.g: IR) try to access
165eb336eabSShuah Khan 	   the device and fill the dmesg with error messages.
166eb336eabSShuah Khan 	   Set the status so poll routines can check and avoid
167eb336eabSShuah Khan 	   access after disconnect.
168eb336eabSShuah Khan 	*/
169eb336eabSShuah Khan 	dev->dev_state = DEV_DISCONNECTED;
170eb336eabSShuah Khan 
1712fcfd317SMauro Carvalho Chehab 	au0828_rc_unregister(dev);
1720c0d06caSMauro Carvalho Chehab 	/* Digital TV */
1730c0d06caSMauro Carvalho Chehab 	au0828_dvb_unregister(dev);
1740c0d06caSMauro Carvalho Chehab 
1750c0d06caSMauro Carvalho Chehab 	usb_set_intfdata(interface, NULL);
1760c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->mutex);
1770c0d06caSMauro Carvalho Chehab 	dev->usbdev = NULL;
1780c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->mutex);
1797b606ffdSMauro Carvalho Chehab 	if (au0828_analog_unregister(dev)) {
1807e9a8ad5SMauro Carvalho Chehab 		/*
1817e9a8ad5SMauro Carvalho Chehab 		 * No need to call au0828_usb_release() if V4L2 is enabled,
1827e9a8ad5SMauro Carvalho Chehab 		 * as this is already called via au0828_usb_v4l2_release()
1837e9a8ad5SMauro Carvalho Chehab 		 */
184823beb7eSHans Verkuil 		return;
185823beb7eSHans Verkuil 	}
186823beb7eSHans Verkuil 	au0828_usb_release(dev);
1870c0d06caSMauro Carvalho Chehab }
1880c0d06caSMauro Carvalho Chehab 
1899f806795SMauro Carvalho Chehab static int au0828_media_device_init(struct au0828_dev *dev,
190bed69196SRafael Lourenço de Lima Chehab 				    struct usb_device *udev)
191bed69196SRafael Lourenço de Lima Chehab {
192bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER
193bed69196SRafael Lourenço de Lima Chehab 	struct media_device *mdev;
194bed69196SRafael Lourenço de Lima Chehab 
195182dde7cSShuah Khan 	mdev = media_device_get_devres(&udev->dev);
196bed69196SRafael Lourenço de Lima Chehab 	if (!mdev)
1979f806795SMauro Carvalho Chehab 		return -ENOMEM;
198bed69196SRafael Lourenço de Lima Chehab 
1997b12adf6SShuah Khan 	/* check if media device is already initialized */
2007b12adf6SShuah Khan 	if (!mdev->dev)
201182dde7cSShuah Khan 		media_device_usb_init(mdev, udev, udev->product);
2026cf5dad1SMauro Carvalho Chehab 
203bed69196SRafael Lourenço de Lima Chehab 	dev->media_dev = mdev;
204bed69196SRafael Lourenço de Lima Chehab #endif
2059f806795SMauro Carvalho Chehab 	return 0;
206bed69196SRafael Lourenço de Lima Chehab }
207bed69196SRafael Lourenço de Lima Chehab 
2080a82edd0SArnd Bergmann #ifdef CONFIG_MEDIA_CONTROLLER
209f90c5d79SShuah Khan static void au0828_media_graph_notify(struct media_entity *new,
210f90c5d79SShuah Khan 				      void *notify_data)
211f90c5d79SShuah Khan {
212f90c5d79SShuah Khan 	struct au0828_dev *dev = (struct au0828_dev *) notify_data;
213f90c5d79SShuah Khan 	int ret;
2149096cae1SShuah Khan 	struct media_entity *entity, *mixer = NULL, *decoder = NULL;
215f90c5d79SShuah Khan 
2169096cae1SShuah Khan 	if (!new) {
2179096cae1SShuah Khan 		/*
2189096cae1SShuah Khan 		 * Called during au0828 probe time to connect
2199096cae1SShuah Khan 		 * entites that were created prior to registering
2209096cae1SShuah Khan 		 * the notify handler. Find mixer and decoder.
2219096cae1SShuah Khan 		*/
2229096cae1SShuah Khan 		media_device_for_each_entity(entity, dev->media_dev) {
2239096cae1SShuah Khan 			if (entity->function == MEDIA_ENT_F_AUDIO_MIXER)
2249096cae1SShuah Khan 				mixer = entity;
2259096cae1SShuah Khan 			else if (entity->function == MEDIA_ENT_F_ATV_DECODER)
2269096cae1SShuah Khan 				decoder = entity;
2279096cae1SShuah Khan 		}
2289096cae1SShuah Khan 		goto create_link;
2299096cae1SShuah Khan 	}
230f90c5d79SShuah Khan 
231f90c5d79SShuah Khan 	switch (new->function) {
232f90c5d79SShuah Khan 	case MEDIA_ENT_F_AUDIO_MIXER:
2339096cae1SShuah Khan 		mixer = new;
2349096cae1SShuah Khan 		if (dev->decoder)
2359096cae1SShuah Khan 			decoder = dev->decoder;
2369096cae1SShuah Khan 		break;
2379096cae1SShuah Khan 	case MEDIA_ENT_F_ATV_DECODER:
2389096cae1SShuah Khan 		/* In case, Mixer is added first, find mixer and create link */
2399096cae1SShuah Khan 		media_device_for_each_entity(entity, dev->media_dev) {
2409096cae1SShuah Khan 			if (entity->function == MEDIA_ENT_F_AUDIO_MIXER)
2419096cae1SShuah Khan 				mixer = entity;
2429096cae1SShuah Khan 		}
2439096cae1SShuah Khan 		decoder = new;
244f90c5d79SShuah Khan 		break;
245f90c5d79SShuah Khan 	default:
246f90c5d79SShuah Khan 		break;
247f90c5d79SShuah Khan 	}
2489096cae1SShuah Khan 
2499096cae1SShuah Khan create_link:
2509096cae1SShuah Khan 	if (decoder && mixer) {
2519096cae1SShuah Khan 		ret = media_create_pad_link(decoder,
2529096cae1SShuah Khan 					    DEMOD_PAD_AUDIO_OUT,
2539096cae1SShuah Khan 					    mixer, 0,
2549096cae1SShuah Khan 					    MEDIA_LNK_FL_ENABLED);
2559096cae1SShuah Khan 		if (ret)
2569096cae1SShuah Khan 			dev_err(&dev->usbdev->dev,
2579096cae1SShuah Khan 				"Mixer Pad Link Create Error: %d\n", ret);
2589096cae1SShuah Khan 	}
259f90c5d79SShuah Khan }
260f90c5d79SShuah Khan 
261c94903f1SShuah Khan static int au0828_enable_source(struct media_entity *entity,
262c94903f1SShuah Khan 				struct media_pipeline *pipe)
263c94903f1SShuah Khan {
264c94903f1SShuah Khan 	struct media_entity  *source, *find_source;
265c94903f1SShuah Khan 	struct media_entity *sink;
266c94903f1SShuah Khan 	struct media_link *link, *found_link = NULL;
267c94903f1SShuah Khan 	int ret = 0;
268c94903f1SShuah Khan 	struct media_device *mdev = entity->graph_obj.mdev;
269c94903f1SShuah Khan 	struct au0828_dev *dev;
270c94903f1SShuah Khan 
271c94903f1SShuah Khan 	if (!mdev)
272c94903f1SShuah Khan 		return -ENODEV;
273c94903f1SShuah Khan 
274c94903f1SShuah Khan 	mutex_lock(&mdev->graph_mutex);
275c94903f1SShuah Khan 
276c94903f1SShuah Khan 	dev = mdev->source_priv;
277c94903f1SShuah Khan 
278c94903f1SShuah Khan 	/*
279c94903f1SShuah Khan 	 * For Audio and V4L2 entity, find the link to which decoder
280c94903f1SShuah Khan 	 * is the sink. Look for an active link between decoder and
281c94903f1SShuah Khan 	 * source (tuner/s-video/Composite), if one exists, nothing
282c94903f1SShuah Khan 	 * to do. If not, look for any  active links between source
283c94903f1SShuah Khan 	 * and any other entity. If one exists, source is busy. If
284c94903f1SShuah Khan 	 * source is free, setup link and start pipeline from source.
285c94903f1SShuah Khan 	 * For DVB FE entity, the source for the link is the tuner.
286c94903f1SShuah Khan 	 * Check if tuner is available and setup link and start
287c94903f1SShuah Khan 	 * pipeline.
288c94903f1SShuah Khan 	*/
289c94903f1SShuah Khan 	if (entity->function == MEDIA_ENT_F_DTV_DEMOD) {
290c94903f1SShuah Khan 		sink = entity;
291c94903f1SShuah Khan 		find_source = dev->tuner;
292c94903f1SShuah Khan 	} else {
293c94903f1SShuah Khan 		/* Analog isn't configured or register failed */
294c94903f1SShuah Khan 		if (!dev->decoder) {
295c94903f1SShuah Khan 			ret = -ENODEV;
296c94903f1SShuah Khan 			goto end;
297c94903f1SShuah Khan 		}
298c94903f1SShuah Khan 
299c94903f1SShuah Khan 		sink = dev->decoder;
300c94903f1SShuah Khan 
301c94903f1SShuah Khan 		/*
302c94903f1SShuah Khan 		 * Default input is tuner and default input_type
303c94903f1SShuah Khan 		 * is AU0828_VMUX_TELEVISION.
304c94903f1SShuah Khan 		 * FIXME:
305c94903f1SShuah Khan 		 * There is a problem when s_input is called to
306c94903f1SShuah Khan 		 * change the default input. s_input will try to
307c94903f1SShuah Khan 		 * enable_source before attempting to change the
308c94903f1SShuah Khan 		 * input on the device, and will end up enabling
309c94903f1SShuah Khan 		 * default source which is tuner.
310c94903f1SShuah Khan 		 *
311c94903f1SShuah Khan 		 * Additional logic is necessary in au0828
312c94903f1SShuah Khan 		 * to detect that the input has changed and
313c94903f1SShuah Khan 		 * enable the right source.
314c94903f1SShuah Khan 		*/
315c94903f1SShuah Khan 
316c94903f1SShuah Khan 		if (dev->input_type == AU0828_VMUX_TELEVISION)
317c94903f1SShuah Khan 			find_source = dev->tuner;
318c94903f1SShuah Khan 		else if (dev->input_type == AU0828_VMUX_SVIDEO ||
319c94903f1SShuah Khan 			 dev->input_type == AU0828_VMUX_COMPOSITE)
320c94903f1SShuah Khan 			find_source = &dev->input_ent[dev->input_type];
321c94903f1SShuah Khan 		else {
322c94903f1SShuah Khan 			/* unknown input - let user select input */
323c94903f1SShuah Khan 			ret = 0;
324c94903f1SShuah Khan 			goto end;
325c94903f1SShuah Khan 		}
326c94903f1SShuah Khan 	}
327c94903f1SShuah Khan 
328c94903f1SShuah Khan 	/* Is an active link between sink and source */
329c94903f1SShuah Khan 	if (dev->active_link) {
330c94903f1SShuah Khan 		/*
331c94903f1SShuah Khan 		 * If DVB is using the tuner and calling entity is
332c94903f1SShuah Khan 		 * audio/video, the following check will be false,
333c94903f1SShuah Khan 		 * since sink is different. Result is Busy.
334c94903f1SShuah Khan 		 */
335c94903f1SShuah Khan 		if (dev->active_link->sink->entity == sink &&
336c94903f1SShuah Khan 		    dev->active_link->source->entity == find_source) {
337c94903f1SShuah Khan 			/*
338c94903f1SShuah Khan 			 * Either ALSA or Video own tuner. sink is
339c94903f1SShuah Khan 			 * the same for both. Prevent Video stepping
340c94903f1SShuah Khan 			 * on ALSA when ALSA owns the source.
341c94903f1SShuah Khan 			*/
342c94903f1SShuah Khan 			if (dev->active_link_owner != entity &&
343c94903f1SShuah Khan 			    dev->active_link_owner->function ==
344c94903f1SShuah Khan 						MEDIA_ENT_F_AUDIO_CAPTURE) {
345c94903f1SShuah Khan 				pr_debug("ALSA has the tuner\n");
346c94903f1SShuah Khan 				ret = -EBUSY;
347c94903f1SShuah Khan 				goto end;
348c94903f1SShuah Khan 			}
349c94903f1SShuah Khan 			ret = 0;
350c94903f1SShuah Khan 			goto end;
351c94903f1SShuah Khan 		} else {
352c94903f1SShuah Khan 			ret = -EBUSY;
353c94903f1SShuah Khan 			goto end;
354c94903f1SShuah Khan 		}
355c94903f1SShuah Khan 	}
356c94903f1SShuah Khan 
357c94903f1SShuah Khan 	list_for_each_entry(link, &sink->links, list) {
358c94903f1SShuah Khan 		/* Check sink, and source */
359c94903f1SShuah Khan 		if (link->sink->entity == sink &&
360c94903f1SShuah Khan 		    link->source->entity == find_source) {
361c94903f1SShuah Khan 			found_link = link;
362c94903f1SShuah Khan 			break;
363c94903f1SShuah Khan 		}
364c94903f1SShuah Khan 	}
365c94903f1SShuah Khan 
366c94903f1SShuah Khan 	if (!found_link) {
367c94903f1SShuah Khan 		ret = -ENODEV;
368c94903f1SShuah Khan 		goto end;
369c94903f1SShuah Khan 	}
370c94903f1SShuah Khan 
371c94903f1SShuah Khan 	/* activate link between source and sink and start pipeline */
372c94903f1SShuah Khan 	source = found_link->source->entity;
373c94903f1SShuah Khan 	ret = __media_entity_setup_link(found_link, MEDIA_LNK_FL_ENABLED);
374c94903f1SShuah Khan 	if (ret) {
375c94903f1SShuah Khan 		pr_err("Activate tuner link %s->%s. Error %d\n",
376c94903f1SShuah Khan 			source->name, sink->name, ret);
377c94903f1SShuah Khan 		goto end;
378c94903f1SShuah Khan 	}
379c94903f1SShuah Khan 
380c94903f1SShuah Khan 	ret = __media_entity_pipeline_start(entity, pipe);
381c94903f1SShuah Khan 	if (ret) {
382c94903f1SShuah Khan 		pr_err("Start Pipeline: %s->%s Error %d\n",
383c94903f1SShuah Khan 			source->name, entity->name, ret);
384c94903f1SShuah Khan 		ret = __media_entity_setup_link(found_link, 0);
385c94903f1SShuah Khan 		pr_err("Deactivate link Error %d\n", ret);
386c94903f1SShuah Khan 		goto end;
387c94903f1SShuah Khan 	}
388c94903f1SShuah Khan 	/*
389c94903f1SShuah Khan 	 * save active link and active link owner to avoid audio
390c94903f1SShuah Khan 	 * deactivating video owned link from disable_source and
391c94903f1SShuah Khan 	 * vice versa
392c94903f1SShuah Khan 	*/
393c94903f1SShuah Khan 	dev->active_link = found_link;
394c94903f1SShuah Khan 	dev->active_link_owner = entity;
395c94903f1SShuah Khan 	dev->active_source = source;
396c94903f1SShuah Khan 	dev->active_sink = sink;
397c94903f1SShuah Khan 
398c94903f1SShuah Khan 	pr_debug("Enabled Source: %s->%s->%s Ret %d\n",
399c94903f1SShuah Khan 		 dev->active_source->name, dev->active_sink->name,
400c94903f1SShuah Khan 		 dev->active_link_owner->name, ret);
401c94903f1SShuah Khan end:
402c94903f1SShuah Khan 	mutex_unlock(&mdev->graph_mutex);
403c94903f1SShuah Khan 	pr_debug("au0828_enable_source() end %s %d %d\n",
404c94903f1SShuah Khan 		 entity->name, entity->function, ret);
405c94903f1SShuah Khan 	return ret;
406c94903f1SShuah Khan }
407c94903f1SShuah Khan 
408c94903f1SShuah Khan static void au0828_disable_source(struct media_entity *entity)
409c94903f1SShuah Khan {
410c94903f1SShuah Khan 	int ret = 0;
411c94903f1SShuah Khan 	struct media_device *mdev = entity->graph_obj.mdev;
412c94903f1SShuah Khan 	struct au0828_dev *dev;
413c94903f1SShuah Khan 
414c94903f1SShuah Khan 	if (!mdev)
415c94903f1SShuah Khan 		return;
416c94903f1SShuah Khan 
417c94903f1SShuah Khan 	mutex_lock(&mdev->graph_mutex);
418c94903f1SShuah Khan 	dev = mdev->source_priv;
419c94903f1SShuah Khan 
420c94903f1SShuah Khan 	if (!dev->active_link) {
421c94903f1SShuah Khan 		ret = -ENODEV;
422c94903f1SShuah Khan 		goto end;
423c94903f1SShuah Khan 	}
424c94903f1SShuah Khan 
425c94903f1SShuah Khan 	/* link is active - stop pipeline from source (tuner) */
426c94903f1SShuah Khan 	if (dev->active_link->sink->entity == dev->active_sink &&
427c94903f1SShuah Khan 	    dev->active_link->source->entity == dev->active_source) {
428c94903f1SShuah Khan 		/*
429c94903f1SShuah Khan 		 * prevent video from deactivating link when audio
430c94903f1SShuah Khan 		 * has active pipeline
431c94903f1SShuah Khan 		*/
432c94903f1SShuah Khan 		if (dev->active_link_owner != entity)
433c94903f1SShuah Khan 			goto end;
434c94903f1SShuah Khan 		__media_entity_pipeline_stop(entity);
435c94903f1SShuah Khan 		ret = __media_entity_setup_link(dev->active_link, 0);
436c94903f1SShuah Khan 		if (ret)
437c94903f1SShuah Khan 			pr_err("Deactivate link Error %d\n", ret);
438c94903f1SShuah Khan 
439c94903f1SShuah Khan 		pr_debug("Disabled Source: %s->%s->%s Ret %d\n",
440c94903f1SShuah Khan 			 dev->active_source->name, dev->active_sink->name,
441c94903f1SShuah Khan 			 dev->active_link_owner->name, ret);
442c94903f1SShuah Khan 
443c94903f1SShuah Khan 		dev->active_link = NULL;
444c94903f1SShuah Khan 		dev->active_link_owner = NULL;
445c94903f1SShuah Khan 		dev->active_source = NULL;
446c94903f1SShuah Khan 		dev->active_sink = NULL;
447c94903f1SShuah Khan 	}
448c94903f1SShuah Khan 
449c94903f1SShuah Khan end:
450c94903f1SShuah Khan 	mutex_unlock(&mdev->graph_mutex);
451c94903f1SShuah Khan }
4520a82edd0SArnd Bergmann #endif
453c94903f1SShuah Khan 
4547b12adf6SShuah Khan static int au0828_media_device_register(struct au0828_dev *dev,
4557b12adf6SShuah Khan 					struct usb_device *udev)
4567b12adf6SShuah Khan {
4577b12adf6SShuah Khan #ifdef CONFIG_MEDIA_CONTROLLER
4587b12adf6SShuah Khan 	int ret;
4597b12adf6SShuah Khan 
460f90c5d79SShuah Khan 	if (!dev->media_dev)
461f90c5d79SShuah Khan 		return 0;
462f90c5d79SShuah Khan 
463f90c5d79SShuah Khan 	if (!media_devnode_is_registered(&dev->media_dev->devnode)) {
4647b12adf6SShuah Khan 
4657b12adf6SShuah Khan 		/* register media device */
4667b12adf6SShuah Khan 		ret = media_device_register(dev->media_dev);
4677b12adf6SShuah Khan 		if (ret) {
4687b12adf6SShuah Khan 			dev_err(&udev->dev,
4697b12adf6SShuah Khan 				"Media Device Register Error: %d\n", ret);
4707b12adf6SShuah Khan 			return ret;
4717b12adf6SShuah Khan 		}
4729096cae1SShuah Khan 	} else {
4739096cae1SShuah Khan 		/*
4749096cae1SShuah Khan 		 * Call au0828_media_graph_notify() to connect
4759096cae1SShuah Khan 		 * audio graph to our graph. In this case, audio
4769096cae1SShuah Khan 		 * driver registered the device and there is no
4779096cae1SShuah Khan 		 * entity_notify to be called when new entities
4789096cae1SShuah Khan 		 * are added. Invoke it now.
4799096cae1SShuah Khan 		*/
4809096cae1SShuah Khan 		au0828_media_graph_notify(NULL, (void *) dev);
4817b12adf6SShuah Khan 	}
482f90c5d79SShuah Khan 	/* register entity_notify callback */
483f90c5d79SShuah Khan 	dev->entity_notify.notify_data = (void *) dev;
484f90c5d79SShuah Khan 	dev->entity_notify.notify = (void *) au0828_media_graph_notify;
485f90c5d79SShuah Khan 	ret = media_device_register_entity_notify(dev->media_dev,
486f90c5d79SShuah Khan 						  &dev->entity_notify);
487f90c5d79SShuah Khan 	if (ret) {
488f90c5d79SShuah Khan 		dev_err(&udev->dev,
489f90c5d79SShuah Khan 			"Media Device register entity_notify Error: %d\n",
490f90c5d79SShuah Khan 			ret);
491f90c5d79SShuah Khan 		return ret;
492f90c5d79SShuah Khan 	}
493c94903f1SShuah Khan 	/* set enable_source */
494c94903f1SShuah Khan 	dev->media_dev->source_priv = (void *) dev;
495c94903f1SShuah Khan 	dev->media_dev->enable_source = au0828_enable_source;
496c94903f1SShuah Khan 	dev->media_dev->disable_source = au0828_disable_source;
4977b12adf6SShuah Khan #endif
4987b12adf6SShuah Khan 	return 0;
4997b12adf6SShuah Khan }
500bed69196SRafael Lourenço de Lima Chehab 
5010c0d06caSMauro Carvalho Chehab static int au0828_usb_probe(struct usb_interface *interface,
5020c0d06caSMauro Carvalho Chehab 	const struct usb_device_id *id)
5030c0d06caSMauro Carvalho Chehab {
5048a4e7866SMichael Krufky 	int ifnum;
505f251b3e7STim Mester 	int retval = 0;
506f251b3e7STim Mester 
5070c0d06caSMauro Carvalho Chehab 	struct au0828_dev *dev;
5080c0d06caSMauro Carvalho Chehab 	struct usb_device *usbdev = interface_to_usbdev(interface);
5090c0d06caSMauro Carvalho Chehab 
5100c0d06caSMauro Carvalho Chehab 	ifnum = interface->altsetting->desc.bInterfaceNumber;
5110c0d06caSMauro Carvalho Chehab 
5120c0d06caSMauro Carvalho Chehab 	if (ifnum != 0)
5130c0d06caSMauro Carvalho Chehab 		return -ENODEV;
5140c0d06caSMauro Carvalho Chehab 
5150c0d06caSMauro Carvalho Chehab 	dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__,
5160c0d06caSMauro Carvalho Chehab 		le16_to_cpu(usbdev->descriptor.idVendor),
5170c0d06caSMauro Carvalho Chehab 		le16_to_cpu(usbdev->descriptor.idProduct),
5180c0d06caSMauro Carvalho Chehab 		ifnum);
5190c0d06caSMauro Carvalho Chehab 
5200c0d06caSMauro Carvalho Chehab 	/*
5210c0d06caSMauro Carvalho Chehab 	 * Make sure we have 480 Mbps of bandwidth, otherwise things like
5220c0d06caSMauro Carvalho Chehab 	 * video stream wouldn't likely work, since 12 Mbps is generally
5230c0d06caSMauro Carvalho Chehab 	 * not enough even for most Digital TV streams.
5240c0d06caSMauro Carvalho Chehab 	 */
5250c0d06caSMauro Carvalho Chehab 	if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
52683afb32aSMauro Carvalho Chehab 		pr_err("au0828: Device initialization failed.\n");
52783afb32aSMauro Carvalho Chehab 		pr_err("au0828: Device must be connected to a high-speed USB 2.0 port.\n");
5280c0d06caSMauro Carvalho Chehab 		return -ENODEV;
5290c0d06caSMauro Carvalho Chehab 	}
5300c0d06caSMauro Carvalho Chehab 
5310c0d06caSMauro Carvalho Chehab 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
5320c0d06caSMauro Carvalho Chehab 	if (dev == NULL) {
53383afb32aSMauro Carvalho Chehab 		pr_err("%s() Unable to allocate memory\n", __func__);
5340c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
5350c0d06caSMauro Carvalho Chehab 	}
5360c0d06caSMauro Carvalho Chehab 
5370c0d06caSMauro Carvalho Chehab 	mutex_init(&dev->lock);
5380c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
5390c0d06caSMauro Carvalho Chehab 	mutex_init(&dev->mutex);
5400c0d06caSMauro Carvalho Chehab 	mutex_init(&dev->dvb.lock);
5410c0d06caSMauro Carvalho Chehab 	dev->usbdev = usbdev;
5420c0d06caSMauro Carvalho Chehab 	dev->boardnr = id->driver_info;
543e42c8c6eSRafael Lourenço de Lima Chehab 	dev->board = au0828_boards[dev->boardnr];
544e42c8c6eSRafael Lourenço de Lima Chehab 
5459832e155SJavier Martinez Canillas 	/* Initialize the media controller */
5469f806795SMauro Carvalho Chehab 	retval = au0828_media_device_init(dev, usbdev);
5479f806795SMauro Carvalho Chehab 	if (retval) {
5489f806795SMauro Carvalho Chehab 		pr_err("%s() au0828_media_device_init failed\n",
5499f806795SMauro Carvalho Chehab 		       __func__);
5509f806795SMauro Carvalho Chehab 		mutex_unlock(&dev->lock);
5519f806795SMauro Carvalho Chehab 		kfree(dev);
5529f806795SMauro Carvalho Chehab 		return retval;
5539f806795SMauro Carvalho Chehab 	}
5540c0d06caSMauro Carvalho Chehab 
5557b606ffdSMauro Carvalho Chehab 	retval = au0828_v4l2_device_register(interface, dev);
5560c0d06caSMauro Carvalho Chehab 	if (retval) {
5577b606ffdSMauro Carvalho Chehab 		au0828_usb_v4l2_media_release(dev);
5580c0d06caSMauro Carvalho Chehab 		mutex_unlock(&dev->lock);
5590c0d06caSMauro Carvalho Chehab 		kfree(dev);
560e8c26f45SHans Verkuil 		return retval;
5610c0d06caSMauro Carvalho Chehab 	}
5620c0d06caSMauro Carvalho Chehab 
5630c0d06caSMauro Carvalho Chehab 	/* Power Up the bridge */
5640c0d06caSMauro Carvalho Chehab 	au0828_write(dev, REG_600, 1 << 4);
5650c0d06caSMauro Carvalho Chehab 
5660c0d06caSMauro Carvalho Chehab 	/* Bring up the GPIO's and supporting devices */
5670c0d06caSMauro Carvalho Chehab 	au0828_gpio_setup(dev);
5680c0d06caSMauro Carvalho Chehab 
5690c0d06caSMauro Carvalho Chehab 	/* I2C */
5700c0d06caSMauro Carvalho Chehab 	au0828_i2c_register(dev);
5710c0d06caSMauro Carvalho Chehab 
5720c0d06caSMauro Carvalho Chehab 	/* Setup */
5730c0d06caSMauro Carvalho Chehab 	au0828_card_setup(dev);
5740c0d06caSMauro Carvalho Chehab 
5750c0d06caSMauro Carvalho Chehab 	/* Analog TV */
57682e92f4cSMauro Carvalho Chehab 	retval = au0828_analog_register(dev, interface);
57782e92f4cSMauro Carvalho Chehab 	if (retval) {
57882e92f4cSMauro Carvalho Chehab 		pr_err("%s() au0282_dev_register failed to register on V4L2\n",
57982e92f4cSMauro Carvalho Chehab 			__func__);
58082e92f4cSMauro Carvalho Chehab 		goto done;
58182e92f4cSMauro Carvalho Chehab 	}
58282e92f4cSMauro Carvalho Chehab 
5830c0d06caSMauro Carvalho Chehab 	/* Digital TV */
584f251b3e7STim Mester 	retval = au0828_dvb_register(dev);
585f251b3e7STim Mester 	if (retval)
586f251b3e7STim Mester 		pr_err("%s() au0282_dev_register failed\n",
587f251b3e7STim Mester 		       __func__);
588f251b3e7STim Mester 
5892fcfd317SMauro Carvalho Chehab 	/* Remote controller */
5902fcfd317SMauro Carvalho Chehab 	au0828_rc_register(dev);
5910c0d06caSMauro Carvalho Chehab 
5922fcfd317SMauro Carvalho Chehab 	/*
5932fcfd317SMauro Carvalho Chehab 	 * Store the pointer to the au0828_dev so it can be accessed in
5942fcfd317SMauro Carvalho Chehab 	 * au0828_usb_disconnect
5952fcfd317SMauro Carvalho Chehab 	 */
5960c0d06caSMauro Carvalho Chehab 	usb_set_intfdata(interface, dev);
5970c0d06caSMauro Carvalho Chehab 
59883afb32aSMauro Carvalho Chehab 	pr_info("Registered device AU0828 [%s]\n",
5990c0d06caSMauro Carvalho Chehab 		dev->board.name == NULL ? "Unset" : dev->board.name);
6000c0d06caSMauro Carvalho Chehab 
6010c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
6020c0d06caSMauro Carvalho Chehab 
6037b12adf6SShuah Khan 	retval = au0828_media_device_register(dev, usbdev);
6049832e155SJavier Martinez Canillas 
6059832e155SJavier Martinez Canillas done:
6069832e155SJavier Martinez Canillas 	if (retval < 0)
6079832e155SJavier Martinez Canillas 		au0828_usb_disconnect(interface);
6089832e155SJavier Martinez Canillas 
609f251b3e7STim Mester 	return retval;
6100c0d06caSMauro Carvalho Chehab }
6110c0d06caSMauro Carvalho Chehab 
612aaeac199SMauro Carvalho Chehab static int au0828_suspend(struct usb_interface *interface,
613aaeac199SMauro Carvalho Chehab 				pm_message_t message)
614aaeac199SMauro Carvalho Chehab {
615aaeac199SMauro Carvalho Chehab 	struct au0828_dev *dev = usb_get_intfdata(interface);
616aaeac199SMauro Carvalho Chehab 
617aaeac199SMauro Carvalho Chehab 	if (!dev)
618aaeac199SMauro Carvalho Chehab 		return 0;
619aaeac199SMauro Carvalho Chehab 
62081187240SMauro Carvalho Chehab 	pr_info("Suspend\n");
62181187240SMauro Carvalho Chehab 
622aaeac199SMauro Carvalho Chehab 	au0828_rc_suspend(dev);
6231a1ba95eSMauro Carvalho Chehab 	au0828_v4l2_suspend(dev);
624b799de75SMauro Carvalho Chehab 	au0828_dvb_suspend(dev);
625aaeac199SMauro Carvalho Chehab 
626aaeac199SMauro Carvalho Chehab 	/* FIXME: should suspend also ATV/DTV */
627aaeac199SMauro Carvalho Chehab 
628aaeac199SMauro Carvalho Chehab 	return 0;
629aaeac199SMauro Carvalho Chehab }
630aaeac199SMauro Carvalho Chehab 
631aaeac199SMauro Carvalho Chehab static int au0828_resume(struct usb_interface *interface)
632aaeac199SMauro Carvalho Chehab {
633aaeac199SMauro Carvalho Chehab 	struct au0828_dev *dev = usb_get_intfdata(interface);
634aaeac199SMauro Carvalho Chehab 	if (!dev)
635aaeac199SMauro Carvalho Chehab 		return 0;
636aaeac199SMauro Carvalho Chehab 
63781187240SMauro Carvalho Chehab 	pr_info("Resume\n");
63881187240SMauro Carvalho Chehab 
639fa500461SMauro Carvalho Chehab 	/* Power Up the bridge */
640fa500461SMauro Carvalho Chehab 	au0828_write(dev, REG_600, 1 << 4);
641fa500461SMauro Carvalho Chehab 
642fa500461SMauro Carvalho Chehab 	/* Bring up the GPIO's and supporting devices */
643fa500461SMauro Carvalho Chehab 	au0828_gpio_setup(dev);
644fa500461SMauro Carvalho Chehab 
645aaeac199SMauro Carvalho Chehab 	au0828_rc_resume(dev);
6461a1ba95eSMauro Carvalho Chehab 	au0828_v4l2_resume(dev);
647b799de75SMauro Carvalho Chehab 	au0828_dvb_resume(dev);
648aaeac199SMauro Carvalho Chehab 
649aaeac199SMauro Carvalho Chehab 	/* FIXME: should resume also ATV/DTV */
650aaeac199SMauro Carvalho Chehab 
651aaeac199SMauro Carvalho Chehab 	return 0;
652aaeac199SMauro Carvalho Chehab }
653aaeac199SMauro Carvalho Chehab 
6540c0d06caSMauro Carvalho Chehab static struct usb_driver au0828_usb_driver = {
65583afb32aSMauro Carvalho Chehab 	.name		= KBUILD_MODNAME,
6560c0d06caSMauro Carvalho Chehab 	.probe		= au0828_usb_probe,
6570c0d06caSMauro Carvalho Chehab 	.disconnect	= au0828_usb_disconnect,
6580c0d06caSMauro Carvalho Chehab 	.id_table	= au0828_usb_id_table,
659aaeac199SMauro Carvalho Chehab 	.suspend	= au0828_suspend,
660aaeac199SMauro Carvalho Chehab 	.resume		= au0828_resume,
661aaeac199SMauro Carvalho Chehab 	.reset_resume	= au0828_resume,
6620c0d06caSMauro Carvalho Chehab };
6630c0d06caSMauro Carvalho Chehab 
6640c0d06caSMauro Carvalho Chehab static int __init au0828_init(void)
6650c0d06caSMauro Carvalho Chehab {
6660c0d06caSMauro Carvalho Chehab 	int ret;
6670c0d06caSMauro Carvalho Chehab 
6680c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 1)
66983afb32aSMauro Carvalho Chehab 		pr_info("%s() Debugging is enabled\n", __func__);
6700c0d06caSMauro Carvalho Chehab 
6710c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 2)
67283afb32aSMauro Carvalho Chehab 		pr_info("%s() USB Debugging is enabled\n", __func__);
6730c0d06caSMauro Carvalho Chehab 
6740c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 4)
67583afb32aSMauro Carvalho Chehab 		pr_info("%s() I2C Debugging is enabled\n", __func__);
6760c0d06caSMauro Carvalho Chehab 
6770c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 8)
67883afb32aSMauro Carvalho Chehab 		pr_info("%s() Bridge Debugging is enabled\n",
6790c0d06caSMauro Carvalho Chehab 		       __func__);
6800c0d06caSMauro Carvalho Chehab 
6812fcfd317SMauro Carvalho Chehab 	if (au0828_debug & 16)
68283afb32aSMauro Carvalho Chehab 		pr_info("%s() IR Debugging is enabled\n",
6832fcfd317SMauro Carvalho Chehab 		       __func__);
6842fcfd317SMauro Carvalho Chehab 
68583afb32aSMauro Carvalho Chehab 	pr_info("au0828 driver loaded\n");
6860c0d06caSMauro Carvalho Chehab 
6870c0d06caSMauro Carvalho Chehab 	ret = usb_register(&au0828_usb_driver);
6880c0d06caSMauro Carvalho Chehab 	if (ret)
68983afb32aSMauro Carvalho Chehab 		pr_err("usb_register failed, error = %d\n", ret);
6900c0d06caSMauro Carvalho Chehab 
6910c0d06caSMauro Carvalho Chehab 	return ret;
6920c0d06caSMauro Carvalho Chehab }
6930c0d06caSMauro Carvalho Chehab 
6940c0d06caSMauro Carvalho Chehab static void __exit au0828_exit(void)
6950c0d06caSMauro Carvalho Chehab {
6960c0d06caSMauro Carvalho Chehab 	usb_deregister(&au0828_usb_driver);
6970c0d06caSMauro Carvalho Chehab }
6980c0d06caSMauro Carvalho Chehab 
6990c0d06caSMauro Carvalho Chehab module_init(au0828_init);
7000c0d06caSMauro Carvalho Chehab module_exit(au0828_exit);
7010c0d06caSMauro Carvalho Chehab 
7020c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products");
7030c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
7040c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
7052fcfd317SMauro Carvalho Chehab MODULE_VERSION("0.0.3");
706