1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  *  Driver for the Auvitek USB bridge
40c0d06caSMauro Carvalho Chehab  *
50c0d06caSMauro Carvalho Chehab  *  Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
60c0d06caSMauro Carvalho Chehab  */
70c0d06caSMauro Carvalho Chehab 
883afb32aSMauro Carvalho Chehab #include "au0828.h"
9f90c5d79SShuah Khan #include "au8522.h"
1083afb32aSMauro Carvalho Chehab 
110c0d06caSMauro Carvalho Chehab #include <linux/module.h>
120c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
130c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h>
140c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
150c0d06caSMauro Carvalho Chehab #include <linux/mutex.h>
160c0d06caSMauro Carvalho Chehab 
17188d2d55SMauro Carvalho Chehab /* Due to enum tuner_pad_index */
18188d2d55SMauro Carvalho Chehab #include <media/tuner.h>
19188d2d55SMauro Carvalho Chehab 
200c0d06caSMauro Carvalho Chehab /*
210c0d06caSMauro Carvalho Chehab  * 1 = General debug messages
220c0d06caSMauro Carvalho Chehab  * 2 = USB handling
230c0d06caSMauro Carvalho Chehab  * 4 = I2C related
240c0d06caSMauro Carvalho Chehab  * 8 = Bridge related
252fcfd317SMauro Carvalho Chehab  * 16 = IR related
260c0d06caSMauro Carvalho Chehab  */
270c0d06caSMauro Carvalho Chehab int au0828_debug;
280c0d06caSMauro Carvalho Chehab module_param_named(debug, au0828_debug, int, 0644);
292fcfd317SMauro Carvalho Chehab MODULE_PARM_DESC(debug,
302fcfd317SMauro Carvalho Chehab 		 "set debug bitmask: 1=general, 2=USB, 4=I2C, 8=bridge, 16=IR");
310c0d06caSMauro Carvalho Chehab 
320c0d06caSMauro Carvalho Chehab static unsigned int disable_usb_speed_check;
330c0d06caSMauro Carvalho Chehab module_param(disable_usb_speed_check, int, 0444);
340c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(disable_usb_speed_check,
350c0d06caSMauro Carvalho Chehab 		 "override min bandwidth requirement of 480M bps");
360c0d06caSMauro Carvalho Chehab 
370c0d06caSMauro Carvalho Chehab #define _AU0828_BULKPIPE 0x03
380c0d06caSMauro Carvalho Chehab #define _BULKPIPESIZE 0xffff
390c0d06caSMauro Carvalho Chehab 
400c0d06caSMauro Carvalho Chehab static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
410c0d06caSMauro Carvalho Chehab 			    u16 index);
420c0d06caSMauro Carvalho Chehab static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
430c0d06caSMauro Carvalho Chehab 	u16 index, unsigned char *cp, u16 size);
440c0d06caSMauro Carvalho Chehab 
450c0d06caSMauro Carvalho Chehab /* USB Direction */
460c0d06caSMauro Carvalho Chehab #define CMD_REQUEST_IN		0x00
470c0d06caSMauro Carvalho Chehab #define CMD_REQUEST_OUT		0x01
480c0d06caSMauro Carvalho Chehab 
au0828_readreg(struct au0828_dev * dev,u16 reg)490c0d06caSMauro Carvalho Chehab u32 au0828_readreg(struct au0828_dev *dev, u16 reg)
500c0d06caSMauro Carvalho Chehab {
510c0d06caSMauro Carvalho Chehab 	u8 result = 0;
520c0d06caSMauro Carvalho Chehab 
530c0d06caSMauro Carvalho Chehab 	recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, &result, 1);
540c0d06caSMauro Carvalho Chehab 	dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, result);
550c0d06caSMauro Carvalho Chehab 
560c0d06caSMauro Carvalho Chehab 	return result;
570c0d06caSMauro Carvalho Chehab }
580c0d06caSMauro Carvalho Chehab 
au0828_writereg(struct au0828_dev * dev,u16 reg,u32 val)590c0d06caSMauro Carvalho Chehab u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val)
600c0d06caSMauro Carvalho Chehab {
610c0d06caSMauro Carvalho Chehab 	dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
620c0d06caSMauro Carvalho Chehab 	return send_control_msg(dev, CMD_REQUEST_OUT, val, reg);
630c0d06caSMauro Carvalho Chehab }
640c0d06caSMauro Carvalho Chehab 
send_control_msg(struct au0828_dev * dev,u16 request,u32 value,u16 index)650c0d06caSMauro Carvalho Chehab static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
660c0d06caSMauro Carvalho Chehab 	u16 index)
670c0d06caSMauro Carvalho Chehab {
680c0d06caSMauro Carvalho Chehab 	int status = -ENODEV;
690c0d06caSMauro Carvalho Chehab 
700c0d06caSMauro Carvalho Chehab 	if (dev->usbdev) {
710c0d06caSMauro Carvalho Chehab 
720c0d06caSMauro Carvalho Chehab 		/* cp must be memory that has been allocated by kmalloc */
730c0d06caSMauro Carvalho Chehab 		status = usb_control_msg(dev->usbdev,
740c0d06caSMauro Carvalho Chehab 				usb_sndctrlpipe(dev->usbdev, 0),
750c0d06caSMauro Carvalho Chehab 				request,
760c0d06caSMauro Carvalho Chehab 				USB_DIR_OUT | USB_TYPE_VENDOR |
770c0d06caSMauro Carvalho Chehab 					USB_RECIP_DEVICE,
780c0d06caSMauro Carvalho Chehab 				value, index, NULL, 0, 1000);
790c0d06caSMauro Carvalho Chehab 
800c0d06caSMauro Carvalho Chehab 		status = min(status, 0);
810c0d06caSMauro Carvalho Chehab 
820c0d06caSMauro Carvalho Chehab 		if (status < 0) {
8383afb32aSMauro Carvalho Chehab 			pr_err("%s() Failed sending control message, error %d.\n",
840c0d06caSMauro Carvalho Chehab 				__func__, status);
850c0d06caSMauro Carvalho Chehab 		}
860c0d06caSMauro Carvalho Chehab 
870c0d06caSMauro Carvalho Chehab 	}
880c0d06caSMauro Carvalho Chehab 
890c0d06caSMauro Carvalho Chehab 	return status;
900c0d06caSMauro Carvalho Chehab }
910c0d06caSMauro Carvalho Chehab 
recv_control_msg(struct au0828_dev * dev,u16 request,u32 value,u16 index,unsigned char * cp,u16 size)920c0d06caSMauro Carvalho Chehab static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
930c0d06caSMauro Carvalho Chehab 	u16 index, unsigned char *cp, u16 size)
940c0d06caSMauro Carvalho Chehab {
950c0d06caSMauro Carvalho Chehab 	int status = -ENODEV;
960c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->mutex);
970c0d06caSMauro Carvalho Chehab 	if (dev->usbdev) {
980c0d06caSMauro Carvalho Chehab 		status = usb_control_msg(dev->usbdev,
990c0d06caSMauro Carvalho Chehab 				usb_rcvctrlpipe(dev->usbdev, 0),
1000c0d06caSMauro Carvalho Chehab 				request,
1010c0d06caSMauro Carvalho Chehab 				USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1020c0d06caSMauro Carvalho Chehab 				value, index,
1030c0d06caSMauro Carvalho Chehab 				dev->ctrlmsg, size, 1000);
1040c0d06caSMauro Carvalho Chehab 
1050c0d06caSMauro Carvalho Chehab 		status = min(status, 0);
1060c0d06caSMauro Carvalho Chehab 
1070c0d06caSMauro Carvalho Chehab 		if (status < 0) {
10883afb32aSMauro Carvalho Chehab 			pr_err("%s() Failed receiving control message, error %d.\n",
1090c0d06caSMauro Carvalho Chehab 				__func__, status);
1100c0d06caSMauro Carvalho Chehab 		}
1110c0d06caSMauro Carvalho Chehab 
1120c0d06caSMauro Carvalho Chehab 		/* the host controller requires heap allocated memory, which
1130c0d06caSMauro Carvalho Chehab 		   is why we didn't just pass "cp" into usb_control_msg */
1140c0d06caSMauro Carvalho Chehab 		memcpy(cp, dev->ctrlmsg, size);
1150c0d06caSMauro Carvalho Chehab 	}
1160c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->mutex);
1170c0d06caSMauro Carvalho Chehab 	return status;
1180c0d06caSMauro Carvalho Chehab }
1190c0d06caSMauro Carvalho Chehab 
120bc5ccdbcSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
121bc5ccdbcSMauro Carvalho Chehab static void au0828_media_graph_notify(struct media_entity *new,
122bc5ccdbcSMauro Carvalho Chehab 				      void *notify_data);
123bc5ccdbcSMauro Carvalho Chehab #endif
124bc5ccdbcSMauro Carvalho Chehab 
au0828_unregister_media_device(struct au0828_dev * dev)125bed69196SRafael Lourenço de Lima Chehab static void au0828_unregister_media_device(struct au0828_dev *dev)
126bed69196SRafael Lourenço de Lima Chehab {
127bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER
128bc5ccdbcSMauro Carvalho Chehab 	struct media_device *mdev = dev->media_dev;
129bc5ccdbcSMauro Carvalho Chehab 	struct media_entity_notify *notify, *nextp;
130bc5ccdbcSMauro Carvalho Chehab 
131a087ce70SMauro Carvalho Chehab 	if (!mdev || !media_devnode_is_registered(mdev->devnode))
132bc5ccdbcSMauro Carvalho Chehab 		return;
133bc5ccdbcSMauro Carvalho Chehab 
134bc5ccdbcSMauro Carvalho Chehab 	/* Remove au0828 entity_notify callbacks */
135bc5ccdbcSMauro Carvalho Chehab 	list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list) {
136bc5ccdbcSMauro Carvalho Chehab 		if (notify->notify != au0828_media_graph_notify)
137bc5ccdbcSMauro Carvalho Chehab 			continue;
138bc5ccdbcSMauro Carvalho Chehab 		media_device_unregister_entity_notify(mdev, notify);
139bc5ccdbcSMauro Carvalho Chehab 	}
140bc5ccdbcSMauro Carvalho Chehab 
141ffa8576aSShuah Khan 	/* clear enable_source, disable_source */
14290cd366bSShuah Khan 	mutex_lock(&mdev->graph_mutex);
143ffa8576aSShuah Khan 	dev->media_dev->source_priv = NULL;
144ffa8576aSShuah Khan 	dev->media_dev->enable_source = NULL;
145ffa8576aSShuah Khan 	dev->media_dev->disable_source = NULL;
14690cd366bSShuah Khan 	mutex_unlock(&mdev->graph_mutex);
147ffa8576aSShuah Khan 
148812658d8SShuah Khan 	media_device_delete(dev->media_dev, KBUILD_MODNAME, THIS_MODULE);
149bed69196SRafael Lourenço de Lima Chehab 	dev->media_dev = NULL;
150bed69196SRafael Lourenço de Lima Chehab #endif
151bed69196SRafael Lourenço de Lima Chehab }
152bed69196SRafael Lourenço de Lima Chehab 
au0828_usb_release(struct au0828_dev * dev)1537b606ffdSMauro Carvalho Chehab void au0828_usb_release(struct au0828_dev *dev)
154823beb7eSHans Verkuil {
155bed69196SRafael Lourenço de Lima Chehab 	au0828_unregister_media_device(dev);
156bed69196SRafael Lourenço de Lima Chehab 
157823beb7eSHans Verkuil 	/* I2C */
158823beb7eSHans Verkuil 	au0828_i2c_unregister(dev);
159823beb7eSHans Verkuil 
160823beb7eSHans Verkuil 	kfree(dev);
161823beb7eSHans Verkuil }
162823beb7eSHans Verkuil 
au0828_usb_disconnect(struct usb_interface * interface)1630c0d06caSMauro Carvalho Chehab static void au0828_usb_disconnect(struct usb_interface *interface)
1640c0d06caSMauro Carvalho Chehab {
1650c0d06caSMauro Carvalho Chehab 	struct au0828_dev *dev = usb_get_intfdata(interface);
1660c0d06caSMauro Carvalho Chehab 
1670c0d06caSMauro Carvalho Chehab 	dprintk(1, "%s()\n", __func__);
1680c0d06caSMauro Carvalho Chehab 
169eb336eabSShuah Khan 	/* there is a small window after disconnect, before
170eb336eabSShuah Khan 	   dev->usbdev is NULL, for poll (e.g: IR) try to access
171eb336eabSShuah Khan 	   the device and fill the dmesg with error messages.
172eb336eabSShuah Khan 	   Set the status so poll routines can check and avoid
173eb336eabSShuah Khan 	   access after disconnect.
174eb336eabSShuah Khan 	*/
175e8e3039fSMauro Carvalho Chehab 	set_bit(DEV_DISCONNECTED, &dev->dev_state);
176eb336eabSShuah Khan 
1772fcfd317SMauro Carvalho Chehab 	au0828_rc_unregister(dev);
1780c0d06caSMauro Carvalho Chehab 	/* Digital TV */
1790c0d06caSMauro Carvalho Chehab 	au0828_dvb_unregister(dev);
1800c0d06caSMauro Carvalho Chehab 
1810c0d06caSMauro Carvalho Chehab 	usb_set_intfdata(interface, NULL);
1820c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->mutex);
1830c0d06caSMauro Carvalho Chehab 	dev->usbdev = NULL;
1840c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->mutex);
1857b606ffdSMauro Carvalho Chehab 	if (au0828_analog_unregister(dev)) {
1867e9a8ad5SMauro Carvalho Chehab 		/*
1877e9a8ad5SMauro Carvalho Chehab 		 * No need to call au0828_usb_release() if V4L2 is enabled,
1887e9a8ad5SMauro Carvalho Chehab 		 * as this is already called via au0828_usb_v4l2_release()
1897e9a8ad5SMauro Carvalho Chehab 		 */
190823beb7eSHans Verkuil 		return;
191823beb7eSHans Verkuil 	}
192823beb7eSHans Verkuil 	au0828_usb_release(dev);
1930c0d06caSMauro Carvalho Chehab }
1940c0d06caSMauro Carvalho Chehab 
au0828_media_device_init(struct au0828_dev * dev,struct usb_device * udev)1959f806795SMauro Carvalho Chehab static int au0828_media_device_init(struct au0828_dev *dev,
196bed69196SRafael Lourenço de Lima Chehab 				    struct usb_device *udev)
197bed69196SRafael Lourenço de Lima Chehab {
198bed69196SRafael Lourenço de Lima Chehab #ifdef CONFIG_MEDIA_CONTROLLER
199bed69196SRafael Lourenço de Lima Chehab 	struct media_device *mdev;
200bed69196SRafael Lourenço de Lima Chehab 
201812658d8SShuah Khan 	mdev = media_device_usb_allocate(udev, KBUILD_MODNAME, THIS_MODULE);
2028f2e4527SDan Carpenter 	if (IS_ERR(mdev))
2038f2e4527SDan Carpenter 		return PTR_ERR(mdev);
204bed69196SRafael Lourenço de Lima Chehab 
205bed69196SRafael Lourenço de Lima Chehab 	dev->media_dev = mdev;
206bed69196SRafael Lourenço de Lima Chehab #endif
2079f806795SMauro Carvalho Chehab 	return 0;
208bed69196SRafael Lourenço de Lima Chehab }
209bed69196SRafael Lourenço de Lima Chehab 
2100a82edd0SArnd Bergmann #ifdef CONFIG_MEDIA_CONTROLLER
au0828_media_graph_notify(struct media_entity * new,void * notify_data)211f90c5d79SShuah Khan static void au0828_media_graph_notify(struct media_entity *new,
212f90c5d79SShuah Khan 				      void *notify_data)
213f90c5d79SShuah Khan {
2140356c10dSYu Zhe 	struct au0828_dev *dev = notify_data;
215f90c5d79SShuah Khan 	int ret;
2169096cae1SShuah Khan 	struct media_entity *entity, *mixer = NULL, *decoder = NULL;
217f90c5d79SShuah Khan 
2189096cae1SShuah Khan 	if (!new) {
2199096cae1SShuah Khan 		/*
2209096cae1SShuah Khan 		 * Called during au0828 probe time to connect
2213e4d8f48SMauro Carvalho Chehab 		 * entities that were created prior to registering
2229096cae1SShuah Khan 		 * the notify handler. Find mixer and decoder.
2239096cae1SShuah Khan 		*/
2249096cae1SShuah Khan 		media_device_for_each_entity(entity, dev->media_dev) {
2259096cae1SShuah Khan 			if (entity->function == MEDIA_ENT_F_AUDIO_MIXER)
2269096cae1SShuah Khan 				mixer = entity;
2279096cae1SShuah Khan 			else if (entity->function == MEDIA_ENT_F_ATV_DECODER)
2289096cae1SShuah Khan 				decoder = entity;
2299096cae1SShuah Khan 		}
2309096cae1SShuah Khan 		goto create_link;
2319096cae1SShuah Khan 	}
232f90c5d79SShuah Khan 
233f90c5d79SShuah Khan 	switch (new->function) {
234f90c5d79SShuah Khan 	case MEDIA_ENT_F_AUDIO_MIXER:
2359096cae1SShuah Khan 		mixer = new;
2369096cae1SShuah Khan 		if (dev->decoder)
2379096cae1SShuah Khan 			decoder = dev->decoder;
2389096cae1SShuah Khan 		break;
2399096cae1SShuah Khan 	case MEDIA_ENT_F_ATV_DECODER:
2409096cae1SShuah Khan 		/* In case, Mixer is added first, find mixer and create link */
2419096cae1SShuah Khan 		media_device_for_each_entity(entity, dev->media_dev) {
2429096cae1SShuah Khan 			if (entity->function == MEDIA_ENT_F_AUDIO_MIXER)
2439096cae1SShuah Khan 				mixer = entity;
2449096cae1SShuah Khan 		}
2459096cae1SShuah Khan 		decoder = new;
246f90c5d79SShuah Khan 		break;
247f90c5d79SShuah Khan 	default:
248f90c5d79SShuah Khan 		break;
249f90c5d79SShuah Khan 	}
2509096cae1SShuah Khan 
2519096cae1SShuah Khan create_link:
2529096cae1SShuah Khan 	if (decoder && mixer) {
253*4fd463e9SSakari Ailus 		ret = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,
2545e68d8e2SMauro Carvalho Chehab 					  PAD_SIGNAL_AUDIO);
2555e68d8e2SMauro Carvalho Chehab 		if (ret >= 0)
2565e68d8e2SMauro Carvalho Chehab 			ret = media_create_pad_link(decoder, ret,
2579096cae1SShuah Khan 						    mixer, 0,
2589096cae1SShuah Khan 						    MEDIA_LNK_FL_ENABLED);
2595e68d8e2SMauro Carvalho Chehab 		if (ret < 0)
2609096cae1SShuah Khan 			dev_err(&dev->usbdev->dev,
2619096cae1SShuah Khan 				"Mixer Pad Link Create Error: %d\n", ret);
2629096cae1SShuah Khan 	}
263f90c5d79SShuah Khan }
264f90c5d79SShuah Khan 
au0828_is_link_shareable(struct media_entity * owner,struct media_entity * entity)265383b0e5bSShuah Khan static bool au0828_is_link_shareable(struct media_entity *owner,
266383b0e5bSShuah Khan 				     struct media_entity *entity)
267383b0e5bSShuah Khan {
268383b0e5bSShuah Khan 	bool shareable = false;
269383b0e5bSShuah Khan 
270383b0e5bSShuah Khan 	/* Tuner link can be shared by audio, video, and VBI */
271383b0e5bSShuah Khan 	switch (owner->function) {
272383b0e5bSShuah Khan 	case MEDIA_ENT_F_IO_V4L:
273383b0e5bSShuah Khan 	case MEDIA_ENT_F_AUDIO_CAPTURE:
274383b0e5bSShuah Khan 	case MEDIA_ENT_F_IO_VBI:
275383b0e5bSShuah Khan 		if (entity->function == MEDIA_ENT_F_IO_V4L ||
276383b0e5bSShuah Khan 		    entity->function == MEDIA_ENT_F_AUDIO_CAPTURE ||
277383b0e5bSShuah Khan 		    entity->function == MEDIA_ENT_F_IO_VBI)
278383b0e5bSShuah Khan 			shareable = true;
279383b0e5bSShuah Khan 		break;
280383b0e5bSShuah Khan 	case MEDIA_ENT_F_DTV_DEMOD:
281383b0e5bSShuah Khan 	default:
282383b0e5bSShuah Khan 		break;
283383b0e5bSShuah Khan 	}
284383b0e5bSShuah Khan 	return shareable;
285383b0e5bSShuah Khan }
286383b0e5bSShuah Khan 
28790cd366bSShuah Khan /* Callers should hold graph_mutex */
au0828_enable_source(struct media_entity * entity,struct media_pipeline * pipe)288c94903f1SShuah Khan static int au0828_enable_source(struct media_entity *entity,
289c94903f1SShuah Khan 				struct media_pipeline *pipe)
290c94903f1SShuah Khan {
291c94903f1SShuah Khan 	struct media_entity  *source, *find_source;
292c94903f1SShuah Khan 	struct media_entity *sink;
293c94903f1SShuah Khan 	struct media_link *link, *found_link = NULL;
294c94903f1SShuah Khan 	int ret = 0;
295c94903f1SShuah Khan 	struct media_device *mdev = entity->graph_obj.mdev;
296c94903f1SShuah Khan 	struct au0828_dev *dev;
297c94903f1SShuah Khan 
298c94903f1SShuah Khan 	if (!mdev)
299c94903f1SShuah Khan 		return -ENODEV;
300c94903f1SShuah Khan 
301c94903f1SShuah Khan 	dev = mdev->source_priv;
302c94903f1SShuah Khan 
303c94903f1SShuah Khan 	/*
304c94903f1SShuah Khan 	 * For Audio and V4L2 entity, find the link to which decoder
305c94903f1SShuah Khan 	 * is the sink. Look for an active link between decoder and
306c94903f1SShuah Khan 	 * source (tuner/s-video/Composite), if one exists, nothing
307c94903f1SShuah Khan 	 * to do. If not, look for any  active links between source
308c94903f1SShuah Khan 	 * and any other entity. If one exists, source is busy. If
309c94903f1SShuah Khan 	 * source is free, setup link and start pipeline from source.
310c94903f1SShuah Khan 	 * For DVB FE entity, the source for the link is the tuner.
311c94903f1SShuah Khan 	 * Check if tuner is available and setup link and start
312c94903f1SShuah Khan 	 * pipeline.
313c94903f1SShuah Khan 	*/
314c94903f1SShuah Khan 	if (entity->function == MEDIA_ENT_F_DTV_DEMOD) {
315c94903f1SShuah Khan 		sink = entity;
316c94903f1SShuah Khan 		find_source = dev->tuner;
317c94903f1SShuah Khan 	} else {
318c94903f1SShuah Khan 		/* Analog isn't configured or register failed */
319c94903f1SShuah Khan 		if (!dev->decoder) {
320c94903f1SShuah Khan 			ret = -ENODEV;
321c94903f1SShuah Khan 			goto end;
322c94903f1SShuah Khan 		}
323c94903f1SShuah Khan 
324c94903f1SShuah Khan 		sink = dev->decoder;
325c94903f1SShuah Khan 
326c94903f1SShuah Khan 		/*
327c94903f1SShuah Khan 		 * Default input is tuner and default input_type
328c94903f1SShuah Khan 		 * is AU0828_VMUX_TELEVISION.
329383b0e5bSShuah Khan 		 *
330c94903f1SShuah Khan 		 * There is a problem when s_input is called to
331c94903f1SShuah Khan 		 * change the default input. s_input will try to
332c94903f1SShuah Khan 		 * enable_source before attempting to change the
333c94903f1SShuah Khan 		 * input on the device, and will end up enabling
334c94903f1SShuah Khan 		 * default source which is tuner.
335c94903f1SShuah Khan 		 *
336383b0e5bSShuah Khan 		 * Additional logic is necessary in au0828 to detect
337383b0e5bSShuah Khan 		 * that the input has changed and enable the right
338383b0e5bSShuah Khan 		 * source. au0828 handles this case in its s_input.
339383b0e5bSShuah Khan 		 * It will disable the old source and enable the new
340383b0e5bSShuah Khan 		 * source.
341383b0e5bSShuah Khan 		 *
342c94903f1SShuah Khan 		*/
343c94903f1SShuah Khan 		if (dev->input_type == AU0828_VMUX_TELEVISION)
344c94903f1SShuah Khan 			find_source = dev->tuner;
345c94903f1SShuah Khan 		else if (dev->input_type == AU0828_VMUX_SVIDEO ||
346c94903f1SShuah Khan 			 dev->input_type == AU0828_VMUX_COMPOSITE)
347c94903f1SShuah Khan 			find_source = &dev->input_ent[dev->input_type];
348c94903f1SShuah Khan 		else {
349c94903f1SShuah Khan 			/* unknown input - let user select input */
350c94903f1SShuah Khan 			ret = 0;
351c94903f1SShuah Khan 			goto end;
352c94903f1SShuah Khan 		}
353c94903f1SShuah Khan 	}
354c94903f1SShuah Khan 
355383b0e5bSShuah Khan 	/* Is there an active link between sink and source */
356c94903f1SShuah Khan 	if (dev->active_link) {
357383b0e5bSShuah Khan 		if (dev->active_link_owner == entity) {
358383b0e5bSShuah Khan 			/* This check is necessary to handle multiple
359383b0e5bSShuah Khan 			 * enable_source calls from v4l_ioctls during
360383b0e5bSShuah Khan 			 * the course of video/vbi application run-time.
361c94903f1SShuah Khan 			*/
362383b0e5bSShuah Khan 			pr_debug("%s already owns the tuner\n", entity->name);
363383b0e5bSShuah Khan 			ret = 0;
364c94903f1SShuah Khan 			goto end;
365383b0e5bSShuah Khan 		} else if (au0828_is_link_shareable(dev->active_link_owner,
366383b0e5bSShuah Khan 			   entity)) {
367383b0e5bSShuah Khan 			/* Either ALSA or Video own tuner. Sink is the same
368383b0e5bSShuah Khan 			 * for both. Allow sharing the active link between
369383b0e5bSShuah Khan 			 * their common source (tuner) and sink (decoder).
370383b0e5bSShuah Khan 			 * Starting pipeline between sharing entity and sink
371383b0e5bSShuah Khan 			 * will fail with pipe mismatch, while owner has an
372383b0e5bSShuah Khan 			 * active pipeline. Switch pipeline ownership from
373383b0e5bSShuah Khan 			 * user to owner when owner disables the source.
374383b0e5bSShuah Khan 			 */
375383b0e5bSShuah Khan 			dev->active_link_shared = true;
376383b0e5bSShuah Khan 			/* save the user info to use from disable */
377383b0e5bSShuah Khan 			dev->active_link_user = entity;
378383b0e5bSShuah Khan 			dev->active_link_user_pipe = pipe;
379383b0e5bSShuah Khan 			pr_debug("%s owns the tuner %s can share!\n",
380383b0e5bSShuah Khan 				 dev->active_link_owner->name,
381383b0e5bSShuah Khan 				 entity->name);
382c94903f1SShuah Khan 			ret = 0;
383c94903f1SShuah Khan 			goto end;
384c94903f1SShuah Khan 		} else {
385c94903f1SShuah Khan 			ret = -EBUSY;
386c94903f1SShuah Khan 			goto end;
387c94903f1SShuah Khan 		}
388c94903f1SShuah Khan 	}
389c94903f1SShuah Khan 
390c94903f1SShuah Khan 	list_for_each_entry(link, &sink->links, list) {
391c94903f1SShuah Khan 		/* Check sink, and source */
392c94903f1SShuah Khan 		if (link->sink->entity == sink &&
393c94903f1SShuah Khan 		    link->source->entity == find_source) {
394c94903f1SShuah Khan 			found_link = link;
395c94903f1SShuah Khan 			break;
396c94903f1SShuah Khan 		}
397c94903f1SShuah Khan 	}
398c94903f1SShuah Khan 
399c94903f1SShuah Khan 	if (!found_link) {
400c94903f1SShuah Khan 		ret = -ENODEV;
401c94903f1SShuah Khan 		goto end;
402c94903f1SShuah Khan 	}
403c94903f1SShuah Khan 
404c94903f1SShuah Khan 	/* activate link between source and sink and start pipeline */
405c94903f1SShuah Khan 	source = found_link->source->entity;
406c94903f1SShuah Khan 	ret = __media_entity_setup_link(found_link, MEDIA_LNK_FL_ENABLED);
407c94903f1SShuah Khan 	if (ret) {
408383b0e5bSShuah Khan 		pr_err("Activate link from %s->%s. Error %d\n",
409c94903f1SShuah Khan 			source->name, sink->name, ret);
410c94903f1SShuah Khan 		goto end;
411c94903f1SShuah Khan 	}
412c94903f1SShuah Khan 
4139e3576a1STomi Valkeinen 	ret = __media_pipeline_start(entity->pads, pipe);
414c94903f1SShuah Khan 	if (ret) {
415c94903f1SShuah Khan 		pr_err("Start Pipeline: %s->%s Error %d\n",
416c94903f1SShuah Khan 			source->name, entity->name, ret);
417c94903f1SShuah Khan 		ret = __media_entity_setup_link(found_link, 0);
418383b0e5bSShuah Khan 		if (ret)
419c94903f1SShuah Khan 			pr_err("Deactivate link Error %d\n", ret);
420c94903f1SShuah Khan 		goto end;
421c94903f1SShuah Khan 	}
422383b0e5bSShuah Khan 
423383b0e5bSShuah Khan 	/* save link state to allow audio and video share the link
424383b0e5bSShuah Khan 	 * and not disable the link while the other is using it.
425383b0e5bSShuah Khan 	 * active_link_owner is used to deactivate the link.
426c94903f1SShuah Khan 	*/
427c94903f1SShuah Khan 	dev->active_link = found_link;
428c94903f1SShuah Khan 	dev->active_link_owner = entity;
429c94903f1SShuah Khan 	dev->active_source = source;
430c94903f1SShuah Khan 	dev->active_sink = sink;
431c94903f1SShuah Khan 
432383b0e5bSShuah Khan 	pr_info("Enabled Source: %s->%s->%s Ret %d\n",
433c94903f1SShuah Khan 		 dev->active_source->name, dev->active_sink->name,
434c94903f1SShuah Khan 		 dev->active_link_owner->name, ret);
435c94903f1SShuah Khan end:
436383b0e5bSShuah Khan 	pr_debug("%s end: ent:%s fnc:%d ret %d\n",
437383b0e5bSShuah Khan 		 __func__, entity->name, entity->function, ret);
438c94903f1SShuah Khan 	return ret;
439c94903f1SShuah Khan }
440c94903f1SShuah Khan 
44190cd366bSShuah Khan /* Callers should hold graph_mutex */
au0828_disable_source(struct media_entity * entity)442c94903f1SShuah Khan static void au0828_disable_source(struct media_entity *entity)
443c94903f1SShuah Khan {
444c94903f1SShuah Khan 	int ret = 0;
445c94903f1SShuah Khan 	struct media_device *mdev = entity->graph_obj.mdev;
446c94903f1SShuah Khan 	struct au0828_dev *dev;
447c94903f1SShuah Khan 
448c94903f1SShuah Khan 	if (!mdev)
449c94903f1SShuah Khan 		return;
450c94903f1SShuah Khan 
451c94903f1SShuah Khan 	dev = mdev->source_priv;
452c94903f1SShuah Khan 
45390cd366bSShuah Khan 	if (!dev->active_link)
45490cd366bSShuah Khan 		return;
455c94903f1SShuah Khan 
456383b0e5bSShuah Khan 	/* link is active - stop pipeline from source
457383b0e5bSShuah Khan 	 * (tuner/s-video/Composite) to the entity
458383b0e5bSShuah Khan 	 * When DVB/s-video/Composite owns tuner, it won't be in
459383b0e5bSShuah Khan 	 * shared state.
460383b0e5bSShuah Khan 	 */
461c94903f1SShuah Khan 	if (dev->active_link->sink->entity == dev->active_sink &&
462c94903f1SShuah Khan 	    dev->active_link->source->entity == dev->active_source) {
463c94903f1SShuah Khan 		/*
464383b0e5bSShuah Khan 		 * Prevent video from deactivating link when audio
465383b0e5bSShuah Khan 		 * has active pipeline and vice versa. In addition
466383b0e5bSShuah Khan 		 * handle the case when more than one video/vbi
467383b0e5bSShuah Khan 		 * application is sharing the link.
468c94903f1SShuah Khan 		*/
469383b0e5bSShuah Khan 		bool owner_is_audio = false;
470383b0e5bSShuah Khan 
471383b0e5bSShuah Khan 		if (dev->active_link_owner->function ==
472383b0e5bSShuah Khan 		    MEDIA_ENT_F_AUDIO_CAPTURE)
473383b0e5bSShuah Khan 			owner_is_audio = true;
474383b0e5bSShuah Khan 
475383b0e5bSShuah Khan 		if (dev->active_link_shared) {
476383b0e5bSShuah Khan 			pr_debug("Shared link owner %s user %s %d\n",
477383b0e5bSShuah Khan 				 dev->active_link_owner->name,
478383b0e5bSShuah Khan 				 entity->name, dev->users);
479383b0e5bSShuah Khan 
480383b0e5bSShuah Khan 			/* Handle video device users > 1
481383b0e5bSShuah Khan 			 * When audio owns the shared link with
482383b0e5bSShuah Khan 			 * more than one video users, avoid
483383b0e5bSShuah Khan 			 * disabling the source and/or switching
484383b0e5bSShuah Khan 			 * the owner until the last disable_source
485383b0e5bSShuah Khan 			 * call from video _close(). Use dev->users to
486383b0e5bSShuah Khan 			 * determine when to switch/disable.
487383b0e5bSShuah Khan 			 */
488383b0e5bSShuah Khan 			if (dev->active_link_owner != entity) {
489383b0e5bSShuah Khan 				/* video device has users > 1 */
490383b0e5bSShuah Khan 				if (owner_is_audio && dev->users > 1)
491383b0e5bSShuah Khan 					return;
492383b0e5bSShuah Khan 
493383b0e5bSShuah Khan 				dev->active_link_user = NULL;
494383b0e5bSShuah Khan 				dev->active_link_user_pipe = NULL;
495383b0e5bSShuah Khan 				dev->active_link_shared = false;
496383b0e5bSShuah Khan 				return;
497383b0e5bSShuah Khan 			}
498383b0e5bSShuah Khan 
499383b0e5bSShuah Khan 			/* video owns the link and has users > 1 */
500383b0e5bSShuah Khan 			if (!owner_is_audio && dev->users > 1)
501383b0e5bSShuah Khan 				return;
502383b0e5bSShuah Khan 
503383b0e5bSShuah Khan 			/* stop pipeline */
5049e3576a1STomi Valkeinen 			__media_pipeline_stop(dev->active_link_owner->pads);
505383b0e5bSShuah Khan 			pr_debug("Pipeline stop for %s\n",
506383b0e5bSShuah Khan 				dev->active_link_owner->name);
507383b0e5bSShuah Khan 
508383b0e5bSShuah Khan 			ret = __media_pipeline_start(
5099e3576a1STomi Valkeinen 					dev->active_link_user->pads,
510383b0e5bSShuah Khan 					dev->active_link_user_pipe);
511383b0e5bSShuah Khan 			if (ret) {
512383b0e5bSShuah Khan 				pr_err("Start Pipeline: %s->%s %d\n",
513383b0e5bSShuah Khan 					dev->active_source->name,
514383b0e5bSShuah Khan 					dev->active_link_user->name,
515383b0e5bSShuah Khan 					ret);
516383b0e5bSShuah Khan 				goto deactivate_link;
517383b0e5bSShuah Khan 			}
518383b0e5bSShuah Khan 			/* link user is now the owner */
519383b0e5bSShuah Khan 			dev->active_link_owner = dev->active_link_user;
520383b0e5bSShuah Khan 			dev->active_link_user = NULL;
521383b0e5bSShuah Khan 			dev->active_link_user_pipe = NULL;
522383b0e5bSShuah Khan 			dev->active_link_shared = false;
523383b0e5bSShuah Khan 
524383b0e5bSShuah Khan 			pr_debug("Pipeline started for %s\n",
525383b0e5bSShuah Khan 				dev->active_link_owner->name);
526383b0e5bSShuah Khan 			return;
527383b0e5bSShuah Khan 		} else if (!owner_is_audio && dev->users > 1)
528383b0e5bSShuah Khan 			/* video/vbi owns the link and has users > 1 */
529383b0e5bSShuah Khan 			return;
530383b0e5bSShuah Khan 
531c94903f1SShuah Khan 		if (dev->active_link_owner != entity)
53290cd366bSShuah Khan 			return;
533383b0e5bSShuah Khan 
534383b0e5bSShuah Khan 		/* stop pipeline */
5359e3576a1STomi Valkeinen 		__media_pipeline_stop(dev->active_link_owner->pads);
536383b0e5bSShuah Khan 		pr_debug("Pipeline stop for %s\n",
537383b0e5bSShuah Khan 			dev->active_link_owner->name);
538383b0e5bSShuah Khan 
539383b0e5bSShuah Khan deactivate_link:
540c94903f1SShuah Khan 		ret = __media_entity_setup_link(dev->active_link, 0);
541c94903f1SShuah Khan 		if (ret)
542c94903f1SShuah Khan 			pr_err("Deactivate link Error %d\n", ret);
543c94903f1SShuah Khan 
544383b0e5bSShuah Khan 		pr_info("Disabled Source: %s->%s->%s Ret %d\n",
545c94903f1SShuah Khan 			 dev->active_source->name, dev->active_sink->name,
546c94903f1SShuah Khan 			 dev->active_link_owner->name, ret);
547c94903f1SShuah Khan 
548c94903f1SShuah Khan 		dev->active_link = NULL;
549c94903f1SShuah Khan 		dev->active_link_owner = NULL;
550c94903f1SShuah Khan 		dev->active_source = NULL;
551c94903f1SShuah Khan 		dev->active_sink = NULL;
552383b0e5bSShuah Khan 		dev->active_link_shared = false;
553383b0e5bSShuah Khan 		dev->active_link_user = NULL;
554c94903f1SShuah Khan 	}
555c94903f1SShuah Khan }
5560a82edd0SArnd Bergmann #endif
557c94903f1SShuah Khan 
au0828_media_device_register(struct au0828_dev * dev,struct usb_device * udev)5587b12adf6SShuah Khan static int au0828_media_device_register(struct au0828_dev *dev,
5597b12adf6SShuah Khan 					struct usb_device *udev)
5607b12adf6SShuah Khan {
5617b12adf6SShuah Khan #ifdef CONFIG_MEDIA_CONTROLLER
5627b12adf6SShuah Khan 	int ret;
5632e208c64SMauro Carvalho Chehab 	struct media_entity *entity, *demod = NULL;
5642e208c64SMauro Carvalho Chehab 	struct media_link *link;
5657b12adf6SShuah Khan 
566f90c5d79SShuah Khan 	if (!dev->media_dev)
567f90c5d79SShuah Khan 		return 0;
568f90c5d79SShuah Khan 
569a087ce70SMauro Carvalho Chehab 	if (!media_devnode_is_registered(dev->media_dev->devnode)) {
5707b12adf6SShuah Khan 
5717b12adf6SShuah Khan 		/* register media device */
5727b12adf6SShuah Khan 		ret = media_device_register(dev->media_dev);
5737b12adf6SShuah Khan 		if (ret) {
574812658d8SShuah Khan 			media_device_delete(dev->media_dev, KBUILD_MODNAME,
575812658d8SShuah Khan 					    THIS_MODULE);
576812658d8SShuah Khan 			dev->media_dev = NULL;
5777b12adf6SShuah Khan 			dev_err(&udev->dev,
5787b12adf6SShuah Khan 				"Media Device Register Error: %d\n", ret);
5797b12adf6SShuah Khan 			return ret;
5807b12adf6SShuah Khan 		}
5819096cae1SShuah Khan 	} else {
5829096cae1SShuah Khan 		/*
5839096cae1SShuah Khan 		 * Call au0828_media_graph_notify() to connect
5849096cae1SShuah Khan 		 * audio graph to our graph. In this case, audio
5859096cae1SShuah Khan 		 * driver registered the device and there is no
5869096cae1SShuah Khan 		 * entity_notify to be called when new entities
5879096cae1SShuah Khan 		 * are added. Invoke it now.
5889096cae1SShuah Khan 		*/
5899096cae1SShuah Khan 		au0828_media_graph_notify(NULL, (void *) dev);
5907b12adf6SShuah Khan 	}
591840f5b05SShuah Khan 
592840f5b05SShuah Khan 	/*
5932e208c64SMauro Carvalho Chehab 	 * Find tuner, decoder and demod.
5942e208c64SMauro Carvalho Chehab 	 *
5952e208c64SMauro Carvalho Chehab 	 * The tuner and decoder should be cached, as they'll be used by
5962e208c64SMauro Carvalho Chehab 	 *	au0828_enable_source.
5972e208c64SMauro Carvalho Chehab 	 *
5982e208c64SMauro Carvalho Chehab 	 * It also needs to disable the link between tuner and
5992e208c64SMauro Carvalho Chehab 	 * decoder/demod, to avoid disable step when tuner is requested
6002e208c64SMauro Carvalho Chehab 	 * by video or audio. Note that this step can't be done until dvb
6012e208c64SMauro Carvalho Chehab 	 * graph is created during dvb register.
602840f5b05SShuah Khan 	*/
603840f5b05SShuah Khan 	media_device_for_each_entity(entity, dev->media_dev) {
6042e208c64SMauro Carvalho Chehab 		switch (entity->function) {
6052e208c64SMauro Carvalho Chehab 		case MEDIA_ENT_F_TUNER:
6062e208c64SMauro Carvalho Chehab 			dev->tuner = entity;
6072e208c64SMauro Carvalho Chehab 			break;
6082e208c64SMauro Carvalho Chehab 		case MEDIA_ENT_F_ATV_DECODER:
6092e208c64SMauro Carvalho Chehab 			dev->decoder = entity;
6102e208c64SMauro Carvalho Chehab 			break;
6112e208c64SMauro Carvalho Chehab 		case MEDIA_ENT_F_DTV_DEMOD:
612840f5b05SShuah Khan 			demod = entity;
6132e208c64SMauro Carvalho Chehab 			break;
614840f5b05SShuah Khan 		}
6152e208c64SMauro Carvalho Chehab 	}
616840f5b05SShuah Khan 
6172e208c64SMauro Carvalho Chehab 	/* Disable link between tuner->demod and/or tuner->decoder */
6182e208c64SMauro Carvalho Chehab 	if (dev->tuner) {
6192e208c64SMauro Carvalho Chehab 		list_for_each_entry(link, &dev->tuner->links, list) {
6202e208c64SMauro Carvalho Chehab 			if (demod && link->sink->entity == demod)
621840f5b05SShuah Khan 				media_entity_setup_link(link, 0);
6222e208c64SMauro Carvalho Chehab 			if (dev->decoder && link->sink->entity == dev->decoder)
6232e208c64SMauro Carvalho Chehab 				media_entity_setup_link(link, 0);
624840f5b05SShuah Khan 		}
625840f5b05SShuah Khan 	}
626840f5b05SShuah Khan 
627f90c5d79SShuah Khan 	/* register entity_notify callback */
628f90c5d79SShuah Khan 	dev->entity_notify.notify_data = (void *) dev;
629f90c5d79SShuah Khan 	dev->entity_notify.notify = (void *) au0828_media_graph_notify;
630a7687fecSJason Kim 	media_device_register_entity_notify(dev->media_dev,
631f90c5d79SShuah Khan 						  &dev->entity_notify);
632a7687fecSJason Kim 
633c94903f1SShuah Khan 	/* set enable_source */
63490cd366bSShuah Khan 	mutex_lock(&dev->media_dev->graph_mutex);
635c94903f1SShuah Khan 	dev->media_dev->source_priv = (void *) dev;
636c94903f1SShuah Khan 	dev->media_dev->enable_source = au0828_enable_source;
637c94903f1SShuah Khan 	dev->media_dev->disable_source = au0828_disable_source;
63890cd366bSShuah Khan 	mutex_unlock(&dev->media_dev->graph_mutex);
6397b12adf6SShuah Khan #endif
6407b12adf6SShuah Khan 	return 0;
6417b12adf6SShuah Khan }
642bed69196SRafael Lourenço de Lima Chehab 
au0828_usb_probe(struct usb_interface * interface,const struct usb_device_id * id)6430c0d06caSMauro Carvalho Chehab static int au0828_usb_probe(struct usb_interface *interface,
6440c0d06caSMauro Carvalho Chehab 	const struct usb_device_id *id)
6450c0d06caSMauro Carvalho Chehab {
6468a4e7866SMichael Krufky 	int ifnum;
647f251b3e7STim Mester 	int retval = 0;
648f251b3e7STim Mester 
6490c0d06caSMauro Carvalho Chehab 	struct au0828_dev *dev;
6500c0d06caSMauro Carvalho Chehab 	struct usb_device *usbdev = interface_to_usbdev(interface);
6510c0d06caSMauro Carvalho Chehab 
6520c0d06caSMauro Carvalho Chehab 	ifnum = interface->altsetting->desc.bInterfaceNumber;
6530c0d06caSMauro Carvalho Chehab 
6540c0d06caSMauro Carvalho Chehab 	if (ifnum != 0)
6550c0d06caSMauro Carvalho Chehab 		return -ENODEV;
6560c0d06caSMauro Carvalho Chehab 
6570c0d06caSMauro Carvalho Chehab 	dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__,
6580c0d06caSMauro Carvalho Chehab 		le16_to_cpu(usbdev->descriptor.idVendor),
6590c0d06caSMauro Carvalho Chehab 		le16_to_cpu(usbdev->descriptor.idProduct),
6600c0d06caSMauro Carvalho Chehab 		ifnum);
6610c0d06caSMauro Carvalho Chehab 
6620c0d06caSMauro Carvalho Chehab 	/*
6630c0d06caSMauro Carvalho Chehab 	 * Make sure we have 480 Mbps of bandwidth, otherwise things like
6640c0d06caSMauro Carvalho Chehab 	 * video stream wouldn't likely work, since 12 Mbps is generally
6650c0d06caSMauro Carvalho Chehab 	 * not enough even for most Digital TV streams.
6660c0d06caSMauro Carvalho Chehab 	 */
6670c0d06caSMauro Carvalho Chehab 	if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
66883afb32aSMauro Carvalho Chehab 		pr_err("au0828: Device initialization failed.\n");
66983afb32aSMauro Carvalho Chehab 		pr_err("au0828: Device must be connected to a high-speed USB 2.0 port.\n");
6700c0d06caSMauro Carvalho Chehab 		return -ENODEV;
6710c0d06caSMauro Carvalho Chehab 	}
6720c0d06caSMauro Carvalho Chehab 
6730c0d06caSMauro Carvalho Chehab 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
6740c0d06caSMauro Carvalho Chehab 	if (dev == NULL) {
67583afb32aSMauro Carvalho Chehab 		pr_err("%s() Unable to allocate memory\n", __func__);
6760c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
6770c0d06caSMauro Carvalho Chehab 	}
6780c0d06caSMauro Carvalho Chehab 
6790c0d06caSMauro Carvalho Chehab 	mutex_init(&dev->lock);
6800c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
6810c0d06caSMauro Carvalho Chehab 	mutex_init(&dev->mutex);
6820c0d06caSMauro Carvalho Chehab 	mutex_init(&dev->dvb.lock);
6830c0d06caSMauro Carvalho Chehab 	dev->usbdev = usbdev;
6840c0d06caSMauro Carvalho Chehab 	dev->boardnr = id->driver_info;
685e42c8c6eSRafael Lourenço de Lima Chehab 	dev->board = au0828_boards[dev->boardnr];
686e42c8c6eSRafael Lourenço de Lima Chehab 
6879832e155SJavier Martinez Canillas 	/* Initialize the media controller */
6889f806795SMauro Carvalho Chehab 	retval = au0828_media_device_init(dev, usbdev);
6899f806795SMauro Carvalho Chehab 	if (retval) {
6909f806795SMauro Carvalho Chehab 		pr_err("%s() au0828_media_device_init failed\n",
6919f806795SMauro Carvalho Chehab 		       __func__);
6929f806795SMauro Carvalho Chehab 		mutex_unlock(&dev->lock);
6939f806795SMauro Carvalho Chehab 		kfree(dev);
6949f806795SMauro Carvalho Chehab 		return retval;
6959f806795SMauro Carvalho Chehab 	}
6960c0d06caSMauro Carvalho Chehab 
6977b606ffdSMauro Carvalho Chehab 	retval = au0828_v4l2_device_register(interface, dev);
6980c0d06caSMauro Carvalho Chehab 	if (retval) {
6997b606ffdSMauro Carvalho Chehab 		au0828_usb_v4l2_media_release(dev);
7000c0d06caSMauro Carvalho Chehab 		mutex_unlock(&dev->lock);
7010c0d06caSMauro Carvalho Chehab 		kfree(dev);
702e8c26f45SHans Verkuil 		return retval;
7030c0d06caSMauro Carvalho Chehab 	}
7040c0d06caSMauro Carvalho Chehab 
7050c0d06caSMauro Carvalho Chehab 	/* Power Up the bridge */
7060c0d06caSMauro Carvalho Chehab 	au0828_write(dev, REG_600, 1 << 4);
7070c0d06caSMauro Carvalho Chehab 
7080c0d06caSMauro Carvalho Chehab 	/* Bring up the GPIO's and supporting devices */
7090c0d06caSMauro Carvalho Chehab 	au0828_gpio_setup(dev);
7100c0d06caSMauro Carvalho Chehab 
7110c0d06caSMauro Carvalho Chehab 	/* I2C */
7120c0d06caSMauro Carvalho Chehab 	au0828_i2c_register(dev);
7130c0d06caSMauro Carvalho Chehab 
7140c0d06caSMauro Carvalho Chehab 	/* Setup */
7150c0d06caSMauro Carvalho Chehab 	au0828_card_setup(dev);
7160c0d06caSMauro Carvalho Chehab 
7176d0d1ff9SSean Young 	/*
7186d0d1ff9SSean Young 	 * Store the pointer to the au0828_dev so it can be accessed in
7196d0d1ff9SSean Young 	 * au0828_usb_disconnect
7206d0d1ff9SSean Young 	 */
7216d0d1ff9SSean Young 	usb_set_intfdata(interface, dev);
7226d0d1ff9SSean Young 
7230c0d06caSMauro Carvalho Chehab 	/* Analog TV */
72482e92f4cSMauro Carvalho Chehab 	retval = au0828_analog_register(dev, interface);
72582e92f4cSMauro Carvalho Chehab 	if (retval) {
726f347596fSBrad Love 		pr_err("%s() au0828_analog_register failed to register on V4L2\n",
72782e92f4cSMauro Carvalho Chehab 			__func__);
7289793e1d2SGustavo A. R. Silva 		mutex_unlock(&dev->lock);
72982e92f4cSMauro Carvalho Chehab 		goto done;
73082e92f4cSMauro Carvalho Chehab 	}
73182e92f4cSMauro Carvalho Chehab 
7320c0d06caSMauro Carvalho Chehab 	/* Digital TV */
733f251b3e7STim Mester 	retval = au0828_dvb_register(dev);
734f251b3e7STim Mester 	if (retval)
735f347596fSBrad Love 		pr_err("%s() au0828_dvb_register failed\n",
736f251b3e7STim Mester 		       __func__);
737f251b3e7STim Mester 
7382fcfd317SMauro Carvalho Chehab 	/* Remote controller */
7392fcfd317SMauro Carvalho Chehab 	au0828_rc_register(dev);
7400c0d06caSMauro Carvalho Chehab 
74183afb32aSMauro Carvalho Chehab 	pr_info("Registered device AU0828 [%s]\n",
7420c0d06caSMauro Carvalho Chehab 		dev->board.name == NULL ? "Unset" : dev->board.name);
7430c0d06caSMauro Carvalho Chehab 
7440c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
7450c0d06caSMauro Carvalho Chehab 
7467b12adf6SShuah Khan 	retval = au0828_media_device_register(dev, usbdev);
7479832e155SJavier Martinez Canillas 
7489832e155SJavier Martinez Canillas done:
7499832e155SJavier Martinez Canillas 	if (retval < 0)
7509832e155SJavier Martinez Canillas 		au0828_usb_disconnect(interface);
7519832e155SJavier Martinez Canillas 
752f251b3e7STim Mester 	return retval;
7530c0d06caSMauro Carvalho Chehab }
7540c0d06caSMauro Carvalho Chehab 
au0828_suspend(struct usb_interface * interface,pm_message_t message)755aaeac199SMauro Carvalho Chehab static int au0828_suspend(struct usb_interface *interface,
756aaeac199SMauro Carvalho Chehab 				pm_message_t message)
757aaeac199SMauro Carvalho Chehab {
758aaeac199SMauro Carvalho Chehab 	struct au0828_dev *dev = usb_get_intfdata(interface);
759aaeac199SMauro Carvalho Chehab 
760aaeac199SMauro Carvalho Chehab 	if (!dev)
761aaeac199SMauro Carvalho Chehab 		return 0;
762aaeac199SMauro Carvalho Chehab 
76381187240SMauro Carvalho Chehab 	pr_info("Suspend\n");
76481187240SMauro Carvalho Chehab 
765aaeac199SMauro Carvalho Chehab 	au0828_rc_suspend(dev);
7661a1ba95eSMauro Carvalho Chehab 	au0828_v4l2_suspend(dev);
767b799de75SMauro Carvalho Chehab 	au0828_dvb_suspend(dev);
768aaeac199SMauro Carvalho Chehab 
769aaeac199SMauro Carvalho Chehab 	/* FIXME: should suspend also ATV/DTV */
770aaeac199SMauro Carvalho Chehab 
771aaeac199SMauro Carvalho Chehab 	return 0;
772aaeac199SMauro Carvalho Chehab }
773aaeac199SMauro Carvalho Chehab 
au0828_resume(struct usb_interface * interface)774aaeac199SMauro Carvalho Chehab static int au0828_resume(struct usb_interface *interface)
775aaeac199SMauro Carvalho Chehab {
776aaeac199SMauro Carvalho Chehab 	struct au0828_dev *dev = usb_get_intfdata(interface);
777aaeac199SMauro Carvalho Chehab 	if (!dev)
778aaeac199SMauro Carvalho Chehab 		return 0;
779aaeac199SMauro Carvalho Chehab 
78081187240SMauro Carvalho Chehab 	pr_info("Resume\n");
78181187240SMauro Carvalho Chehab 
782fa500461SMauro Carvalho Chehab 	/* Power Up the bridge */
783fa500461SMauro Carvalho Chehab 	au0828_write(dev, REG_600, 1 << 4);
784fa500461SMauro Carvalho Chehab 
785fa500461SMauro Carvalho Chehab 	/* Bring up the GPIO's and supporting devices */
786fa500461SMauro Carvalho Chehab 	au0828_gpio_setup(dev);
787fa500461SMauro Carvalho Chehab 
788aaeac199SMauro Carvalho Chehab 	au0828_rc_resume(dev);
7891a1ba95eSMauro Carvalho Chehab 	au0828_v4l2_resume(dev);
790b799de75SMauro Carvalho Chehab 	au0828_dvb_resume(dev);
791aaeac199SMauro Carvalho Chehab 
792aaeac199SMauro Carvalho Chehab 	/* FIXME: should resume also ATV/DTV */
793aaeac199SMauro Carvalho Chehab 
794aaeac199SMauro Carvalho Chehab 	return 0;
795aaeac199SMauro Carvalho Chehab }
796aaeac199SMauro Carvalho Chehab 
7970c0d06caSMauro Carvalho Chehab static struct usb_driver au0828_usb_driver = {
79883afb32aSMauro Carvalho Chehab 	.name		= KBUILD_MODNAME,
7990c0d06caSMauro Carvalho Chehab 	.probe		= au0828_usb_probe,
8000c0d06caSMauro Carvalho Chehab 	.disconnect	= au0828_usb_disconnect,
8010c0d06caSMauro Carvalho Chehab 	.id_table	= au0828_usb_id_table,
802aaeac199SMauro Carvalho Chehab 	.suspend	= au0828_suspend,
803aaeac199SMauro Carvalho Chehab 	.resume		= au0828_resume,
804aaeac199SMauro Carvalho Chehab 	.reset_resume	= au0828_resume,
8050c0d06caSMauro Carvalho Chehab };
8060c0d06caSMauro Carvalho Chehab 
au0828_init(void)8070c0d06caSMauro Carvalho Chehab static int __init au0828_init(void)
8080c0d06caSMauro Carvalho Chehab {
8090c0d06caSMauro Carvalho Chehab 	int ret;
8100c0d06caSMauro Carvalho Chehab 
8110c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 1)
81283afb32aSMauro Carvalho Chehab 		pr_info("%s() Debugging is enabled\n", __func__);
8130c0d06caSMauro Carvalho Chehab 
8140c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 2)
81583afb32aSMauro Carvalho Chehab 		pr_info("%s() USB Debugging is enabled\n", __func__);
8160c0d06caSMauro Carvalho Chehab 
8170c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 4)
81883afb32aSMauro Carvalho Chehab 		pr_info("%s() I2C Debugging is enabled\n", __func__);
8190c0d06caSMauro Carvalho Chehab 
8200c0d06caSMauro Carvalho Chehab 	if (au0828_debug & 8)
82183afb32aSMauro Carvalho Chehab 		pr_info("%s() Bridge Debugging is enabled\n",
8220c0d06caSMauro Carvalho Chehab 		       __func__);
8230c0d06caSMauro Carvalho Chehab 
8242fcfd317SMauro Carvalho Chehab 	if (au0828_debug & 16)
82583afb32aSMauro Carvalho Chehab 		pr_info("%s() IR Debugging is enabled\n",
8262fcfd317SMauro Carvalho Chehab 		       __func__);
8272fcfd317SMauro Carvalho Chehab 
82883afb32aSMauro Carvalho Chehab 	pr_info("au0828 driver loaded\n");
8290c0d06caSMauro Carvalho Chehab 
8300c0d06caSMauro Carvalho Chehab 	ret = usb_register(&au0828_usb_driver);
8310c0d06caSMauro Carvalho Chehab 	if (ret)
83283afb32aSMauro Carvalho Chehab 		pr_err("usb_register failed, error = %d\n", ret);
8330c0d06caSMauro Carvalho Chehab 
8340c0d06caSMauro Carvalho Chehab 	return ret;
8350c0d06caSMauro Carvalho Chehab }
8360c0d06caSMauro Carvalho Chehab 
au0828_exit(void)8370c0d06caSMauro Carvalho Chehab static void __exit au0828_exit(void)
8380c0d06caSMauro Carvalho Chehab {
8390c0d06caSMauro Carvalho Chehab 	usb_deregister(&au0828_usb_driver);
8400c0d06caSMauro Carvalho Chehab }
8410c0d06caSMauro Carvalho Chehab 
8420c0d06caSMauro Carvalho Chehab module_init(au0828_init);
8430c0d06caSMauro Carvalho Chehab module_exit(au0828_exit);
8440c0d06caSMauro Carvalho Chehab 
8450c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products");
8460c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
8470c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
8482fcfd317SMauro Carvalho Chehab MODULE_VERSION("0.0.3");
849