⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 snd-aoa-fabric-layout.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	/* we need these for headphone/lineout detection */	struct snd_kcontrol *headphone_ctrl;	struct snd_kcontrol *lineout_ctrl;	struct snd_kcontrol *speaker_ctrl;	struct snd_kcontrol *headphone_detected_ctrl;	struct snd_kcontrol *lineout_detected_ctrl;	struct layout_dev_ptr selfptr_headphone;	struct layout_dev_ptr selfptr_lineout;	u32 have_lineout_detect:1,	    have_headphone_detect:1,	    switch_on_headphone:1,	    switch_on_lineout:1;};static LIST_HEAD(layouts_list);static int layouts_list_items;/* this can go away but only if we allow multiple cards, * make the fabric handle all the card stuff, etc... */static struct layout_dev *layout_device;#define control_info	snd_ctl_boolean_mono_info#define AMP_CONTROL(n, description)					\static int n##_control_get(struct snd_kcontrol *kcontrol,		\			   struct snd_ctl_elem_value *ucontrol)		\{									\	struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol);	\	if (gpio->methods && gpio->methods->get_##n)			\		ucontrol->value.integer.value[0] =			\			gpio->methods->get_##n(gpio);			\	return 0;							\}									\static int n##_control_put(struct snd_kcontrol *kcontrol,		\			   struct snd_ctl_elem_value *ucontrol)		\{									\	struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol);	\	if (gpio->methods && gpio->methods->get_##n)			\		gpio->methods->set_##n(gpio,				\			ucontrol->value.integer.value[0]);		\	return 1;							\}									\static struct snd_kcontrol_new n##_ctl = {				\	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,				\	.name = description,						\	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,                      \	.info = control_info,						\	.get = n##_control_get,						\	.put = n##_control_put,						\}AMP_CONTROL(headphone, "Headphone Switch");AMP_CONTROL(speakers, "Speakers Switch");AMP_CONTROL(lineout, "Line-Out Switch");static int detect_choice_get(struct snd_kcontrol *kcontrol,			     struct snd_ctl_elem_value *ucontrol){	struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);	switch (kcontrol->private_value) {	case 0:		ucontrol->value.integer.value[0] = ldev->switch_on_headphone;		break;	case 1:		ucontrol->value.integer.value[0] = ldev->switch_on_lineout;		break;	default:		return -ENODEV;	}	return 0;}static int detect_choice_put(struct snd_kcontrol *kcontrol,			     struct snd_ctl_elem_value *ucontrol){	struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);	switch (kcontrol->private_value) {	case 0:		ldev->switch_on_headphone = !!ucontrol->value.integer.value[0];		break;	case 1:		ldev->switch_on_lineout = !!ucontrol->value.integer.value[0];		break;	default:		return -ENODEV;	}	return 1;}static struct snd_kcontrol_new headphone_detect_choice = {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "Headphone Detect Autoswitch",	.info = control_info,	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,	.get = detect_choice_get,	.put = detect_choice_put,	.private_value = 0,};static struct snd_kcontrol_new lineout_detect_choice = {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "Line-Out Detect Autoswitch",	.info = control_info,	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,	.get = detect_choice_get,	.put = detect_choice_put,	.private_value = 1,};static int detected_get(struct snd_kcontrol *kcontrol,			struct snd_ctl_elem_value *ucontrol){	struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);	int v;	switch (kcontrol->private_value) {	case 0:		v = ldev->gpio.methods->get_detect(&ldev->gpio,						   AOA_NOTIFY_HEADPHONE);		break;	case 1:		v = ldev->gpio.methods->get_detect(&ldev->gpio,						   AOA_NOTIFY_LINE_OUT);		break;	default:		return -ENODEV;	}	ucontrol->value.integer.value[0] = v;	return 0;}static struct snd_kcontrol_new headphone_detected = {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "Headphone Detected",	.info = control_info,	.access = SNDRV_CTL_ELEM_ACCESS_READ,	.get = detected_get,	.private_value = 0,};static struct snd_kcontrol_new lineout_detected = {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "Line-Out Detected",	.info = control_info,	.access = SNDRV_CTL_ELEM_ACCESS_READ,	.get = detected_get,	.private_value = 1,};static int check_codec(struct aoa_codec *codec,		       struct layout_dev *ldev,		       struct codec_connect_info *cci){	const u32 *ref;	char propname[32];	struct codec_connection *cc;	/* if the codec has a 'codec' node, we require a reference */	if (codec->node && (strcmp(codec->node->name, "codec") == 0)) {		snprintf(propname, sizeof(propname),			 "platform-%s-codec-ref", codec->name);		ref = of_get_property(ldev->sound, propname, NULL);		if (!ref) {			printk(KERN_INFO "snd-aoa-fabric-layout: "				"required property %s not present\n", propname);			return -ENODEV;		}		if (*ref != codec->node->linux_phandle) {			printk(KERN_INFO "snd-aoa-fabric-layout: "				"%s doesn't match!\n", propname);			return -ENODEV;		}	} else {		if (layouts_list_items != 1) {			printk(KERN_INFO "snd-aoa-fabric-layout: "				"more than one soundbus, but no references.\n");			return -ENODEV;		}	}	codec->soundbus_dev = ldev->sdev;	codec->gpio = &ldev->gpio;	cc = cci->connections;	if (!cc)		return -EINVAL;	printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n");	codec->connected = 0;	codec->fabric_data = cc;	while (cc->connected) {		codec->connected |= 1<<cc->codec_bit;		cc++;	}	return 0;}static int layout_found_codec(struct aoa_codec *codec){	struct layout_dev *ldev;	int i;	list_for_each_entry(ldev, &layouts_list, list) {		for (i=0; i<MAX_CODECS_PER_BUS; i++) {			if (!ldev->layout->codecs[i].name)				continue;			if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) {				if (check_codec(codec,						ldev,						&ldev->layout->codecs[i]) == 0)					return 0;			}		}	}	return -ENODEV;}static void layout_remove_codec(struct aoa_codec *codec){	int i;	/* here remove the codec from the layout dev's	 * codec reference */	codec->soundbus_dev = NULL;	codec->gpio = NULL;	for (i=0; i<MAX_CODECS_PER_BUS; i++) {	}}static void layout_notify(void *data){	struct layout_dev_ptr *dptr = data;	struct layout_dev *ldev;	int v, update;	struct snd_kcontrol *detected, *c;	struct snd_card *card = aoa_get_card();	ldev = dptr->ptr;	if (data == &ldev->selfptr_headphone) {		v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE);		detected = ldev->headphone_detected_ctrl;		update = ldev->switch_on_headphone;		if (update) {			ldev->gpio.methods->set_speakers(&ldev->gpio, !v);			ldev->gpio.methods->set_headphone(&ldev->gpio, v);			ldev->gpio.methods->set_lineout(&ldev->gpio, 0);		}	} else if (data == &ldev->selfptr_lineout) {		v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT);		detected = ldev->lineout_detected_ctrl;		update = ldev->switch_on_lineout;		if (update) {			ldev->gpio.methods->set_speakers(&ldev->gpio, !v);			ldev->gpio.methods->set_headphone(&ldev->gpio, 0);			ldev->gpio.methods->set_lineout(&ldev->gpio, v);		}	} else		return;	if (detected)		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id);	if (update) {		c = ldev->headphone_ctrl;		if (c)			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);		c = ldev->speaker_ctrl;		if (c)			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);		c = ldev->lineout_ctrl;		if (c)			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);	}}static void layout_attached_codec(struct aoa_codec *codec){	struct codec_connection *cc;	struct snd_kcontrol *ctl;	int headphones, lineout;	struct layout_dev *ldev = layout_device;	/* need to add this codec to our codec array! */	cc = codec->fabric_data;	headphones = codec->gpio->methods->get_detect(codec->gpio,						      AOA_NOTIFY_HEADPHONE); 	lineout = codec->gpio->methods->get_detect(codec->gpio,						   AOA_NOTIFY_LINE_OUT);	while (cc->connected) {		if (cc->connected & CC_SPEAKERS) {			if (headphones <= 0 && lineout <= 0)				ldev->gpio.methods->set_speakers(codec->gpio, 1);			ctl = snd_ctl_new1(&speakers_ctl, codec->gpio);			ldev->speaker_ctrl = ctl;			aoa_snd_ctl_add(ctl);		}		if (cc->connected & CC_HEADPHONE) {			if (headphones == 1)				ldev->gpio.methods->set_headphone(codec->gpio, 1);			ctl = snd_ctl_new1(&headphone_ctl, codec->gpio);			ldev->headphone_ctrl = ctl;			aoa_snd_ctl_add(ctl);			ldev->have_headphone_detect =				!ldev->gpio.methods					->set_notify(&ldev->gpio,						     AOA_NOTIFY_HEADPHONE,						     layout_notify,						     &ldev->selfptr_headphone);			if (ldev->have_headphone_detect) {				ctl = snd_ctl_new1(&headphone_detect_choice,						   ldev);				aoa_snd_ctl_add(ctl);				ctl = snd_ctl_new1(&headphone_detected,						   ldev);				ldev->headphone_detected_ctrl = ctl;				aoa_snd_ctl_add(ctl);			}		}		if (cc->connected & CC_LINEOUT) {			if (lineout == 1)				ldev->gpio.methods->set_lineout(codec->gpio, 1);			ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);			if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)				strlcpy(ctl->id.name,					"Headphone Switch", sizeof(ctl->id.name));			ldev->lineout_ctrl = ctl;			aoa_snd_ctl_add(ctl);			ldev->have_lineout_detect =				!ldev->gpio.methods					->set_notify(&ldev->gpio,						     AOA_NOTIFY_LINE_OUT,						     layout_notify,						     &ldev->selfptr_lineout);			if (ldev->have_lineout_detect) {				ctl = snd_ctl_new1(&lineout_detect_choice,						   ldev);				if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)					strlcpy(ctl->id.name,						"Headphone Detect Autoswitch",						sizeof(ctl->id.name));				aoa_snd_ctl_add(ctl);				ctl = snd_ctl_new1(&lineout_detected,						   ldev);				if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)					strlcpy(ctl->id.name,						"Headphone Detected",						sizeof(ctl->id.name));				ldev->lineout_detected_ctrl = ctl;				aoa_snd_ctl_add(ctl);			}		}		cc++;	}	/* now update initial state */	if (ldev->have_headphone_detect)		layout_notify(&ldev->selfptr_headphone);	if (ldev->have_lineout_detect)		layout_notify(&ldev->selfptr_lineout);}static struct aoa_fabric layout_fabric = {	.name = "SoundByLayout",	.owner = THIS_MODULE,	.found_codec = layout_found_codec,	.remove_codec = layout_remove_codec,	.attached_codec = layout_attached_codec,};static int aoa_fabric_layout_probe(struct soundbus_dev *sdev){	struct device_node *sound = NULL;	const unsigned int *layout_id;	struct layout *layout;	struct layout_dev *ldev = NULL;	int err;	/* hm, currently we can only have one ... */	if (layout_device)		return -ENODEV;	/* by breaking out we keep a reference */	while ((sound = of_get_next_child(sdev->ofdev.node, sound))) {		if (sound->type && strcasecmp(sound->type, "soundchip") == 0)			break;	}	if (!sound) return -ENODEV;	layout_id = of_get_property(sound, "layout-id", NULL);	if (!layout_id)		goto outnodev;	printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d\n",	       *layout_id);	layout = find_layout_by_id(*layout_id);	if (!layout) {		printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n");		goto outnodev;	}	ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL);	if (!ldev)		goto outnodev;	layout_device = ldev;	ldev->sdev = sdev;	ldev->sound = sound;	ldev->layout = layout;	ldev->gpio.node = sound->parent;	switch (layout->layout_id) {	case 41: /* that unknown machine no one seems to have */	case 51: /* PowerBook5,4 */	case 58: /* Mac Mini */		ldev->gpio.methods = ftr_gpio_methods;		printk(KERN_DEBUG		       "snd-aoa-fabric-layout: Using direct GPIOs\n");		break;	default:		ldev->gpio.methods = pmf_gpio_methods;		printk(KERN_DEBUG		       "snd-aoa-fabric-layout: Using PMF GPIOs\n");	}	ldev->selfptr_headphone.ptr = ldev;	ldev->selfptr_lineout.ptr = ldev;	sdev->ofdev.dev.driver_data = ldev;	list_add(&ldev->list, &layouts_list);	layouts_list_items++;	/* assign these before registering ourselves, so	 * callbacks that are done during registration	 * already have the values */	sdev->pcmid = ldev->layout->pcmid;	if (ldev->layout->busname) {		sdev->pcmname = ldev->layout->busname;	} else {		sdev->pcmname = "Master";	}	ldev->gpio.methods->init(&ldev->gpio);	err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev);	if (err && err != -EALREADY) {		printk(KERN_INFO "snd-aoa-fabric-layout: can't use,"				 " another fabric is active!\n");		goto outlistdel;	}	use_layout(layout);	ldev->switch_on_headphone = 1;	ldev->switch_on_lineout = 1;	return 0; outlistdel:	/* we won't be using these then... */	ldev->gpio.methods->exit(&ldev->gpio);	/* reset if we didn't use it */	sdev->pcmname = NULL;	sdev->pcmid = -1;	list_del(&ldev->list);	layouts_list_items--; outnodev: 	of_node_put(sound); 	layout_device = NULL; 	kfree(ldev);	return -ENODEV;}static int aoa_fabric_layout_remove(struct soundbus_dev *sdev){	struct layout_dev *ldev = sdev->ofdev.dev.driver_data;	int i;	for (i=0; i<MAX_CODECS_PER_BUS; i++) {		if (ldev->codecs[i]) {			aoa_fabric_unlink_codec(ldev->codecs[i]);		}		ldev->codecs[i] = NULL;	}	list_del(&ldev->list);	layouts_list_items--;	of_node_put(ldev->sound);	ldev->gpio.methods->set_notify(&ldev->gpio,				       AOA_NOTIFY_HEADPHONE,				       NULL,				       NULL);	ldev->gpio.methods->set_notify(&ldev->gpio,				       AOA_NOTIFY_LINE_OUT,				       NULL,				       NULL);	ldev->gpio.methods->exit(&ldev->gpio);	layout_device = NULL;	kfree(ldev);	sdev->pcmid = -1;	sdev->pcmname = NULL;	return 0;}#ifdef CONFIG_PMstatic int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state){	struct layout_dev *ldev = sdev->ofdev.dev.driver_data;	if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)		ldev->gpio.methods->all_amps_off(&ldev->gpio);	return 0;}static int aoa_fabric_layout_resume(struct soundbus_dev *sdev){	struct layout_dev *ldev = sdev->ofdev.dev.driver_data;	if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)		ldev->gpio.methods->all_amps_restore(&ldev->gpio);	return 0;}#endifstatic struct soundbus_driver aoa_soundbus_driver = {	.name = "snd_aoa_soundbus_drv",	.owner = THIS_MODULE,	.probe = aoa_fabric_layout_probe,	.remove = aoa_fabric_layout_remove,#ifdef CONFIG_PM	.suspend = aoa_fabric_layout_suspend,	.resume = aoa_fabric_layout_resume,#endif	.driver = {		.owner = THIS_MODULE,	}};static int __init aoa_fabric_layout_init(void){	int err;	err = soundbus_register_driver(&aoa_soundbus_driver);	if (err)		return err;	return 0;}static void __exit aoa_fabric_layout_exit(void){	soundbus_unregister_driver(&aoa_soundbus_driver);	aoa_fabric_unregister(&layout_fabric);}module_init(aoa_fabric_layout_init);module_exit(aoa_fabric_layout_exit);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -