brintos

brintos / linux-shallow public Read only

0
0
Text · 17.5 KiB · b1e059f Raw
740 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Apple iSight audio driver4 *5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>6 */7 8#include <asm/byteorder.h>9#include <linux/delay.h>10#include <linux/device.h>11#include <linux/firewire.h>12#include <linux/firewire-constants.h>13#include <linux/module.h>14#include <linux/mod_devicetable.h>15#include <linux/mutex.h>16#include <linux/string.h>17#include <sound/control.h>18#include <sound/core.h>19#include <sound/initval.h>20#include <sound/pcm.h>21#include <sound/tlv.h>22#include "lib.h"23#include "iso-resources.h"24#include "packets-buffer.h"25 26#define OUI_APPLE		0x000a2727#define MODEL_APPLE_ISIGHT	0x00000828#define SW_ISIGHT_AUDIO		0x00001029 30#define REG_AUDIO_ENABLE	0x00031#define  AUDIO_ENABLE		0x8000000032#define REG_DEF_AUDIO_GAIN	0x20433#define REG_GAIN_RAW_START	0x21034#define REG_GAIN_RAW_END	0x21435#define REG_GAIN_DB_START	0x21836#define REG_GAIN_DB_END		0x21c37#define REG_SAMPLE_RATE_INQUIRY	0x28038#define REG_ISO_TX_CONFIG	0x30039#define  SPEED_SHIFT		1640#define REG_SAMPLE_RATE		0x40041#define  RATE_48000		0x8000000042#define REG_GAIN		0x50043#define REG_MUTE		0x50444 45#define MAX_FRAMES_PER_PACKET	47546 47#define QUEUE_LENGTH		2048 49struct isight {50	struct snd_card *card;51	struct fw_unit *unit;52	struct fw_device *device;53	u64 audio_base;54	struct snd_pcm_substream *pcm;55	struct mutex mutex;56	struct iso_packets_buffer buffer;57	struct fw_iso_resources resources;58	struct fw_iso_context *context;59	bool pcm_active;60	bool pcm_running;61	bool first_packet;62	int packet_index;63	u32 total_samples;64	unsigned int buffer_pointer;65	unsigned int period_counter;66	s32 gain_min, gain_max;67	unsigned int gain_tlv[4];68};69 70struct audio_payload {71	__be32 sample_count;72	__be32 signature;73	__be32 sample_total;74	__be32 reserved;75	__be16 samples[2 * MAX_FRAMES_PER_PACKET];76};77 78MODULE_DESCRIPTION("iSight audio driver");79MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");80MODULE_LICENSE("GPL");81 82static struct fw_iso_packet audio_packet = {83	.payload_length = sizeof(struct audio_payload),84	.interrupt = 1,85	.header_length = 4,86};87 88static void isight_update_pointers(struct isight *isight, unsigned int count)89{90	struct snd_pcm_runtime *runtime = isight->pcm->runtime;91	unsigned int ptr;92 93	smp_wmb(); /* update buffer data before buffer pointer */94 95	ptr = isight->buffer_pointer;96	ptr += count;97	if (ptr >= runtime->buffer_size)98		ptr -= runtime->buffer_size;99	WRITE_ONCE(isight->buffer_pointer, ptr);100 101	isight->period_counter += count;102	if (isight->period_counter >= runtime->period_size) {103		isight->period_counter -= runtime->period_size;104		snd_pcm_period_elapsed(isight->pcm);105	}106}107 108static void isight_samples(struct isight *isight,109			   const __be16 *samples, unsigned int count)110{111	struct snd_pcm_runtime *runtime;112	unsigned int count1;113 114	if (!READ_ONCE(isight->pcm_running))115		return;116 117	runtime = isight->pcm->runtime;118	if (isight->buffer_pointer + count <= runtime->buffer_size) {119		memcpy(runtime->dma_area + isight->buffer_pointer * 4,120		       samples, count * 4);121	} else {122		count1 = runtime->buffer_size - isight->buffer_pointer;123		memcpy(runtime->dma_area + isight->buffer_pointer * 4,124		       samples, count1 * 4);125		samples += count1 * 2;126		memcpy(runtime->dma_area, samples, (count - count1) * 4);127	}128 129	isight_update_pointers(isight, count);130}131 132static void isight_pcm_abort(struct isight *isight)133{134	if (READ_ONCE(isight->pcm_active))135		snd_pcm_stop_xrun(isight->pcm);136}137 138static void isight_dropped_samples(struct isight *isight, unsigned int total)139{140	struct snd_pcm_runtime *runtime;141	u32 dropped;142	unsigned int count1;143 144	if (!READ_ONCE(isight->pcm_running))145		return;146 147	runtime = isight->pcm->runtime;148	dropped = total - isight->total_samples;149	if (dropped < runtime->buffer_size) {150		if (isight->buffer_pointer + dropped <= runtime->buffer_size) {151			memset(runtime->dma_area + isight->buffer_pointer * 4,152			       0, dropped * 4);153		} else {154			count1 = runtime->buffer_size - isight->buffer_pointer;155			memset(runtime->dma_area + isight->buffer_pointer * 4,156			       0, count1 * 4);157			memset(runtime->dma_area, 0, (dropped - count1) * 4);158		}159		isight_update_pointers(isight, dropped);160	} else {161		isight_pcm_abort(isight);162	}163}164 165static void isight_packet(struct fw_iso_context *context, u32 cycle,166			  size_t header_length, void *header, void *data)167{168	struct isight *isight = data;169	const struct audio_payload *payload;170	unsigned int index, length, count, total;171	int err;172 173	if (isight->packet_index < 0)174		return;175	index = isight->packet_index;176	payload = isight->buffer.packets[index].buffer;177	length = be32_to_cpup(header) >> 16;178 179	if (likely(length >= 16 &&180		   payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) {181		count = be32_to_cpu(payload->sample_count);182		if (likely(count <= (length - 16) / 4)) {183			total = be32_to_cpu(payload->sample_total);184			if (unlikely(total != isight->total_samples)) {185				if (!isight->first_packet)186					isight_dropped_samples(isight, total);187				isight->first_packet = false;188				isight->total_samples = total;189			}190 191			isight_samples(isight, payload->samples, count);192			isight->total_samples += count;193		}194	}195 196	err = fw_iso_context_queue(isight->context, &audio_packet,197				   &isight->buffer.iso_buffer,198				   isight->buffer.packets[index].offset);199	if (err < 0) {200		dev_err(&isight->unit->device, "queueing error: %d\n", err);201		isight_pcm_abort(isight);202		isight->packet_index = -1;203		return;204	}205	fw_iso_context_queue_flush(isight->context);206 207	if (++index >= QUEUE_LENGTH)208		index = 0;209	isight->packet_index = index;210}211 212static int isight_connect(struct isight *isight)213{214	int ch, err;215	__be32 value;216 217retry_after_bus_reset:218	ch = fw_iso_resources_allocate(&isight->resources,219				       sizeof(struct audio_payload),220				       isight->device->max_speed);221	if (ch < 0) {222		err = ch;223		goto error;224	}225 226	value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));227	err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,228				 isight->audio_base + REG_ISO_TX_CONFIG,229				 &value, 4, FW_FIXED_GENERATION |230				 isight->resources.generation);231	if (err == -EAGAIN) {232		fw_iso_resources_free(&isight->resources);233		goto retry_after_bus_reset;234	} else if (err < 0) {235		goto err_resources;236	}237 238	return 0;239 240err_resources:241	fw_iso_resources_free(&isight->resources);242error:243	return err;244}245 246static int isight_open(struct snd_pcm_substream *substream)247{248	static const struct snd_pcm_hardware hardware = {249		.info = SNDRV_PCM_INFO_MMAP |250			SNDRV_PCM_INFO_MMAP_VALID |251			SNDRV_PCM_INFO_BATCH |252			SNDRV_PCM_INFO_INTERLEAVED |253			SNDRV_PCM_INFO_BLOCK_TRANSFER,254		.formats = SNDRV_PCM_FMTBIT_S16_BE,255		.rates = SNDRV_PCM_RATE_48000,256		.rate_min = 48000,257		.rate_max = 48000,258		.channels_min = 2,259		.channels_max = 2,260		.buffer_bytes_max = 4 * 1024 * 1024,261		.period_bytes_min = MAX_FRAMES_PER_PACKET * 4,262		.period_bytes_max = 1024 * 1024,263		.periods_min = 2,264		.periods_max = UINT_MAX,265	};266	struct isight *isight = substream->private_data;267 268	substream->runtime->hw = hardware;269 270	return iso_packets_buffer_init(&isight->buffer, isight->unit,271				       QUEUE_LENGTH,272				       sizeof(struct audio_payload),273				       DMA_FROM_DEVICE);274}275 276static int isight_close(struct snd_pcm_substream *substream)277{278	struct isight *isight = substream->private_data;279 280	iso_packets_buffer_destroy(&isight->buffer, isight->unit);281 282	return 0;283}284 285static int isight_hw_params(struct snd_pcm_substream *substream,286			    struct snd_pcm_hw_params *hw_params)287{288	struct isight *isight = substream->private_data;289 290	WRITE_ONCE(isight->pcm_active, true);291 292	return 0;293}294 295static int reg_read(struct isight *isight, int offset, __be32 *value)296{297	return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,298				  isight->audio_base + offset, value, 4, 0);299}300 301static int reg_write(struct isight *isight, int offset, __be32 value)302{303	return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,304				  isight->audio_base + offset, &value, 4, 0);305}306 307static void isight_stop_streaming(struct isight *isight)308{309	__be32 value;310 311	if (!isight->context)312		return;313 314	fw_iso_context_stop(isight->context);315	fw_iso_context_destroy(isight->context);316	isight->context = NULL;317	fw_iso_resources_free(&isight->resources);318	value = 0;319	snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,320			   isight->audio_base + REG_AUDIO_ENABLE,321			   &value, 4, FW_QUIET);322}323 324static int isight_hw_free(struct snd_pcm_substream *substream)325{326	struct isight *isight = substream->private_data;327 328	WRITE_ONCE(isight->pcm_active, false);329 330	mutex_lock(&isight->mutex);331	isight_stop_streaming(isight);332	mutex_unlock(&isight->mutex);333 334	return 0;335}336 337static int isight_start_streaming(struct isight *isight)338{339	unsigned int i;340	int err;341 342	if (isight->context) {343		if (isight->packet_index < 0)344			isight_stop_streaming(isight);345		else346			return 0;347	}348 349	err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000));350	if (err < 0)351		goto error;352 353	err = isight_connect(isight);354	if (err < 0)355		goto error;356 357	err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE));358	if (err < 0)359		goto err_resources;360 361	isight->context = fw_iso_context_create(isight->device->card,362						FW_ISO_CONTEXT_RECEIVE,363						isight->resources.channel,364						isight->device->max_speed,365						4, isight_packet, isight);366	if (IS_ERR(isight->context)) {367		err = PTR_ERR(isight->context);368		isight->context = NULL;369		goto err_resources;370	}371 372	for (i = 0; i < QUEUE_LENGTH; ++i) {373		err = fw_iso_context_queue(isight->context, &audio_packet,374					   &isight->buffer.iso_buffer,375					   isight->buffer.packets[i].offset);376		if (err < 0)377			goto err_context;378	}379 380	isight->first_packet = true;381	isight->packet_index = 0;382 383	err = fw_iso_context_start(isight->context, -1, 0,384				   FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/);385	if (err < 0)386		goto err_context;387 388	return 0;389 390err_context:391	fw_iso_context_destroy(isight->context);392	isight->context = NULL;393err_resources:394	fw_iso_resources_free(&isight->resources);395	reg_write(isight, REG_AUDIO_ENABLE, 0);396error:397	return err;398}399 400static int isight_prepare(struct snd_pcm_substream *substream)401{402	struct isight *isight = substream->private_data;403	int err;404 405	isight->buffer_pointer = 0;406	isight->period_counter = 0;407 408	mutex_lock(&isight->mutex);409	err = isight_start_streaming(isight);410	mutex_unlock(&isight->mutex);411 412	return err;413}414 415static int isight_trigger(struct snd_pcm_substream *substream, int cmd)416{417	struct isight *isight = substream->private_data;418 419	switch (cmd) {420	case SNDRV_PCM_TRIGGER_START:421		WRITE_ONCE(isight->pcm_running, true);422		break;423	case SNDRV_PCM_TRIGGER_STOP:424		WRITE_ONCE(isight->pcm_running, false);425		break;426	default:427		return -EINVAL;428	}429	return 0;430}431 432static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream)433{434	struct isight *isight = substream->private_data;435 436	return READ_ONCE(isight->buffer_pointer);437}438 439static int isight_create_pcm(struct isight *isight)440{441	static const struct snd_pcm_ops ops = {442		.open      = isight_open,443		.close     = isight_close,444		.hw_params = isight_hw_params,445		.hw_free   = isight_hw_free,446		.prepare   = isight_prepare,447		.trigger   = isight_trigger,448		.pointer   = isight_pointer,449	};450	struct snd_pcm *pcm;451	int err;452 453	err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm);454	if (err < 0)455		return err;456	pcm->private_data = isight;457	pcm->nonatomic = true;458	strcpy(pcm->name, "iSight");459	isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;460	isight->pcm->ops = &ops;461	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);462 463	return 0;464}465 466static int isight_gain_info(struct snd_kcontrol *ctl,467			    struct snd_ctl_elem_info *info)468{469	struct isight *isight = ctl->private_data;470 471	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;472	info->count = 1;473	info->value.integer.min = isight->gain_min;474	info->value.integer.max = isight->gain_max;475 476	return 0;477}478 479static int isight_gain_get(struct snd_kcontrol *ctl,480			   struct snd_ctl_elem_value *value)481{482	struct isight *isight = ctl->private_data;483	__be32 gain;484	int err;485 486	err = reg_read(isight, REG_GAIN, &gain);487	if (err < 0)488		return err;489 490	value->value.integer.value[0] = (s32)be32_to_cpu(gain);491 492	return 0;493}494 495static int isight_gain_put(struct snd_kcontrol *ctl,496			   struct snd_ctl_elem_value *value)497{498	struct isight *isight = ctl->private_data;499 500	if (value->value.integer.value[0] < isight->gain_min ||501	    value->value.integer.value[0] > isight->gain_max)502		return -EINVAL;503 504	return reg_write(isight, REG_GAIN,505			 cpu_to_be32(value->value.integer.value[0]));506}507 508static int isight_mute_get(struct snd_kcontrol *ctl,509			   struct snd_ctl_elem_value *value)510{511	struct isight *isight = ctl->private_data;512	__be32 mute;513	int err;514 515	err = reg_read(isight, REG_MUTE, &mute);516	if (err < 0)517		return err;518 519	value->value.integer.value[0] = !mute;520 521	return 0;522}523 524static int isight_mute_put(struct snd_kcontrol *ctl,525			   struct snd_ctl_elem_value *value)526{527	struct isight *isight = ctl->private_data;528 529	return reg_write(isight, REG_MUTE,530			 (__force __be32)!value->value.integer.value[0]);531}532 533static int isight_create_mixer(struct isight *isight)534{535	static const struct snd_kcontrol_new gain_control = {536		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,537		.name = "Mic Capture Volume",538		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |539			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,540		.info = isight_gain_info,541		.get = isight_gain_get,542		.put = isight_gain_put,543	};544	static const struct snd_kcontrol_new mute_control = {545		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,546		.name = "Mic Capture Switch",547		.info = snd_ctl_boolean_mono_info,548		.get = isight_mute_get,549		.put = isight_mute_put,550	};551	__be32 value;552	struct snd_kcontrol *ctl;553	int err;554 555	err = reg_read(isight, REG_GAIN_RAW_START, &value);556	if (err < 0)557		return err;558	isight->gain_min = be32_to_cpu(value);559 560	err = reg_read(isight, REG_GAIN_RAW_END, &value);561	if (err < 0)562		return err;563	isight->gain_max = be32_to_cpu(value);564 565	isight->gain_tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_MINMAX;566	isight->gain_tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);567 568	err = reg_read(isight, REG_GAIN_DB_START, &value);569	if (err < 0)570		return err;571	isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MIN] =572						(s32)be32_to_cpu(value) * 100;573 574	err = reg_read(isight, REG_GAIN_DB_END, &value);575	if (err < 0)576		return err;577	isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX] =578						(s32)be32_to_cpu(value) * 100;579 580	ctl = snd_ctl_new1(&gain_control, isight);581	if (ctl)582		ctl->tlv.p = isight->gain_tlv;583	err = snd_ctl_add(isight->card, ctl);584	if (err < 0)585		return err;586 587	err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight));588	if (err < 0)589		return err;590 591	return 0;592}593 594static void isight_card_free(struct snd_card *card)595{596	struct isight *isight = card->private_data;597 598	fw_iso_resources_destroy(&isight->resources);599}600 601static u64 get_unit_base(struct fw_unit *unit)602{603	struct fw_csr_iterator i;604	int key, value;605 606	fw_csr_iterator_init(&i, unit->directory);607	while (fw_csr_iterator_next(&i, &key, &value))608		if (key == CSR_OFFSET)609			return CSR_REGISTER_BASE + value * 4;610	return 0;611}612 613static int isight_probe(struct fw_unit *unit,614			const struct ieee1394_device_id *id)615{616	struct fw_device *fw_dev = fw_parent_device(unit);617	struct snd_card *card;618	struct isight *isight;619	int err;620 621	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,622			   sizeof(*isight), &card);623	if (err < 0)624		return err;625 626	isight = card->private_data;627	isight->card = card;628	mutex_init(&isight->mutex);629	isight->unit = fw_unit_get(unit);630	isight->device = fw_dev;631	isight->audio_base = get_unit_base(unit);632	if (!isight->audio_base) {633		dev_err(&unit->device, "audio unit base not found\n");634		err = -ENXIO;635		goto error;636	}637	fw_iso_resources_init(&isight->resources, unit);638 639	card->private_free = isight_card_free;640 641	strcpy(card->driver, "iSight");642	strcpy(card->shortname, "Apple iSight");643	snprintf(card->longname, sizeof(card->longname),644		 "Apple iSight (GUID %08x%08x) at %s, S%d",645		 fw_dev->config_rom[3], fw_dev->config_rom[4],646		 dev_name(&unit->device), 100 << fw_dev->max_speed);647	strcpy(card->mixername, "iSight");648 649	err = isight_create_pcm(isight);650	if (err < 0)651		goto error;652 653	err = isight_create_mixer(isight);654	if (err < 0)655		goto error;656 657	err = snd_card_register(card);658	if (err < 0)659		goto error;660 661	dev_set_drvdata(&unit->device, isight);662 663	return 0;664error:665	snd_card_free(card);666 667	mutex_destroy(&isight->mutex);668	fw_unit_put(isight->unit);669 670	return err;671}672 673static void isight_bus_reset(struct fw_unit *unit)674{675	struct isight *isight = dev_get_drvdata(&unit->device);676 677	if (fw_iso_resources_update(&isight->resources) < 0) {678		isight_pcm_abort(isight);679 680		mutex_lock(&isight->mutex);681		isight_stop_streaming(isight);682		mutex_unlock(&isight->mutex);683	}684}685 686static void isight_remove(struct fw_unit *unit)687{688	struct isight *isight = dev_get_drvdata(&unit->device);689 690	isight_pcm_abort(isight);691 692	snd_card_disconnect(isight->card);693 694	mutex_lock(&isight->mutex);695	isight_stop_streaming(isight);696	mutex_unlock(&isight->mutex);697 698	// Block till all of ALSA character devices are released.699	snd_card_free(isight->card);700 701	mutex_destroy(&isight->mutex);702	fw_unit_put(isight->unit);703}704 705static const struct ieee1394_device_id isight_id_table[] = {706	{707		.match_flags  = IEEE1394_MATCH_SPECIFIER_ID |708				IEEE1394_MATCH_VERSION,709		.specifier_id = OUI_APPLE,710		.version      = SW_ISIGHT_AUDIO,711	},712	{ }713};714MODULE_DEVICE_TABLE(ieee1394, isight_id_table);715 716static struct fw_driver isight_driver = {717	.driver   = {718		.owner	= THIS_MODULE,719		.name	= KBUILD_MODNAME,720		.bus	= &fw_bus_type,721	},722	.probe    = isight_probe,723	.update   = isight_bus_reset,724	.remove   = isight_remove,725	.id_table = isight_id_table,726};727 728static int __init alsa_isight_init(void)729{730	return driver_register(&isight_driver.driver);731}732 733static void __exit alsa_isight_exit(void)734{735	driver_unregister(&isight_driver.driver);736}737 738module_init(alsa_isight_init);739module_exit(alsa_isight_exit);740