18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Apple iSight audio driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/firewire.h>
128c2ecf20Sopenharmony_ci#include <linux/firewire-constants.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
158c2ecf20Sopenharmony_ci#include <linux/mutex.h>
168c2ecf20Sopenharmony_ci#include <linux/string.h>
178c2ecf20Sopenharmony_ci#include <sound/control.h>
188c2ecf20Sopenharmony_ci#include <sound/core.h>
198c2ecf20Sopenharmony_ci#include <sound/initval.h>
208c2ecf20Sopenharmony_ci#include <sound/pcm.h>
218c2ecf20Sopenharmony_ci#include <sound/tlv.h>
228c2ecf20Sopenharmony_ci#include "lib.h"
238c2ecf20Sopenharmony_ci#include "iso-resources.h"
248c2ecf20Sopenharmony_ci#include "packets-buffer.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define OUI_APPLE		0x000a27
278c2ecf20Sopenharmony_ci#define MODEL_APPLE_ISIGHT	0x000008
288c2ecf20Sopenharmony_ci#define SW_ISIGHT_AUDIO		0x000010
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define REG_AUDIO_ENABLE	0x000
318c2ecf20Sopenharmony_ci#define  AUDIO_ENABLE		0x80000000
328c2ecf20Sopenharmony_ci#define REG_DEF_AUDIO_GAIN	0x204
338c2ecf20Sopenharmony_ci#define REG_GAIN_RAW_START	0x210
348c2ecf20Sopenharmony_ci#define REG_GAIN_RAW_END	0x214
358c2ecf20Sopenharmony_ci#define REG_GAIN_DB_START	0x218
368c2ecf20Sopenharmony_ci#define REG_GAIN_DB_END		0x21c
378c2ecf20Sopenharmony_ci#define REG_SAMPLE_RATE_INQUIRY	0x280
388c2ecf20Sopenharmony_ci#define REG_ISO_TX_CONFIG	0x300
398c2ecf20Sopenharmony_ci#define  SPEED_SHIFT		16
408c2ecf20Sopenharmony_ci#define REG_SAMPLE_RATE		0x400
418c2ecf20Sopenharmony_ci#define  RATE_48000		0x80000000
428c2ecf20Sopenharmony_ci#define REG_GAIN		0x500
438c2ecf20Sopenharmony_ci#define REG_MUTE		0x504
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define MAX_FRAMES_PER_PACKET	475
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define QUEUE_LENGTH		20
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct isight {
508c2ecf20Sopenharmony_ci	struct snd_card *card;
518c2ecf20Sopenharmony_ci	struct fw_unit *unit;
528c2ecf20Sopenharmony_ci	struct fw_device *device;
538c2ecf20Sopenharmony_ci	u64 audio_base;
548c2ecf20Sopenharmony_ci	struct snd_pcm_substream *pcm;
558c2ecf20Sopenharmony_ci	struct mutex mutex;
568c2ecf20Sopenharmony_ci	struct iso_packets_buffer buffer;
578c2ecf20Sopenharmony_ci	struct fw_iso_resources resources;
588c2ecf20Sopenharmony_ci	struct fw_iso_context *context;
598c2ecf20Sopenharmony_ci	bool pcm_active;
608c2ecf20Sopenharmony_ci	bool pcm_running;
618c2ecf20Sopenharmony_ci	bool first_packet;
628c2ecf20Sopenharmony_ci	int packet_index;
638c2ecf20Sopenharmony_ci	u32 total_samples;
648c2ecf20Sopenharmony_ci	unsigned int buffer_pointer;
658c2ecf20Sopenharmony_ci	unsigned int period_counter;
668c2ecf20Sopenharmony_ci	s32 gain_min, gain_max;
678c2ecf20Sopenharmony_ci	unsigned int gain_tlv[4];
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistruct audio_payload {
718c2ecf20Sopenharmony_ci	__be32 sample_count;
728c2ecf20Sopenharmony_ci	__be32 signature;
738c2ecf20Sopenharmony_ci	__be32 sample_total;
748c2ecf20Sopenharmony_ci	__be32 reserved;
758c2ecf20Sopenharmony_ci	__be16 samples[2 * MAX_FRAMES_PER_PACKET];
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("iSight audio driver");
798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic struct fw_iso_packet audio_packet = {
838c2ecf20Sopenharmony_ci	.payload_length = sizeof(struct audio_payload),
848c2ecf20Sopenharmony_ci	.interrupt = 1,
858c2ecf20Sopenharmony_ci	.header_length = 4,
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void isight_update_pointers(struct isight *isight, unsigned int count)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = isight->pcm->runtime;
918c2ecf20Sopenharmony_ci	unsigned int ptr;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	smp_wmb(); /* update buffer data before buffer pointer */
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	ptr = isight->buffer_pointer;
968c2ecf20Sopenharmony_ci	ptr += count;
978c2ecf20Sopenharmony_ci	if (ptr >= runtime->buffer_size)
988c2ecf20Sopenharmony_ci		ptr -= runtime->buffer_size;
998c2ecf20Sopenharmony_ci	WRITE_ONCE(isight->buffer_pointer, ptr);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	isight->period_counter += count;
1028c2ecf20Sopenharmony_ci	if (isight->period_counter >= runtime->period_size) {
1038c2ecf20Sopenharmony_ci		isight->period_counter -= runtime->period_size;
1048c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(isight->pcm);
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic void isight_samples(struct isight *isight,
1098c2ecf20Sopenharmony_ci			   const __be16 *samples, unsigned int count)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
1128c2ecf20Sopenharmony_ci	unsigned int count1;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (!READ_ONCE(isight->pcm_running))
1158c2ecf20Sopenharmony_ci		return;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	runtime = isight->pcm->runtime;
1188c2ecf20Sopenharmony_ci	if (isight->buffer_pointer + count <= runtime->buffer_size) {
1198c2ecf20Sopenharmony_ci		memcpy(runtime->dma_area + isight->buffer_pointer * 4,
1208c2ecf20Sopenharmony_ci		       samples, count * 4);
1218c2ecf20Sopenharmony_ci	} else {
1228c2ecf20Sopenharmony_ci		count1 = runtime->buffer_size - isight->buffer_pointer;
1238c2ecf20Sopenharmony_ci		memcpy(runtime->dma_area + isight->buffer_pointer * 4,
1248c2ecf20Sopenharmony_ci		       samples, count1 * 4);
1258c2ecf20Sopenharmony_ci		samples += count1 * 2;
1268c2ecf20Sopenharmony_ci		memcpy(runtime->dma_area, samples, (count - count1) * 4);
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	isight_update_pointers(isight, count);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void isight_pcm_abort(struct isight *isight)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	if (READ_ONCE(isight->pcm_active))
1358c2ecf20Sopenharmony_ci		snd_pcm_stop_xrun(isight->pcm);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void isight_dropped_samples(struct isight *isight, unsigned int total)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
1418c2ecf20Sopenharmony_ci	u32 dropped;
1428c2ecf20Sopenharmony_ci	unsigned int count1;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!READ_ONCE(isight->pcm_running))
1458c2ecf20Sopenharmony_ci		return;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	runtime = isight->pcm->runtime;
1488c2ecf20Sopenharmony_ci	dropped = total - isight->total_samples;
1498c2ecf20Sopenharmony_ci	if (dropped < runtime->buffer_size) {
1508c2ecf20Sopenharmony_ci		if (isight->buffer_pointer + dropped <= runtime->buffer_size) {
1518c2ecf20Sopenharmony_ci			memset(runtime->dma_area + isight->buffer_pointer * 4,
1528c2ecf20Sopenharmony_ci			       0, dropped * 4);
1538c2ecf20Sopenharmony_ci		} else {
1548c2ecf20Sopenharmony_ci			count1 = runtime->buffer_size - isight->buffer_pointer;
1558c2ecf20Sopenharmony_ci			memset(runtime->dma_area + isight->buffer_pointer * 4,
1568c2ecf20Sopenharmony_ci			       0, count1 * 4);
1578c2ecf20Sopenharmony_ci			memset(runtime->dma_area, 0, (dropped - count1) * 4);
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci		isight_update_pointers(isight, dropped);
1608c2ecf20Sopenharmony_ci	} else {
1618c2ecf20Sopenharmony_ci		isight_pcm_abort(isight);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void isight_packet(struct fw_iso_context *context, u32 cycle,
1668c2ecf20Sopenharmony_ci			  size_t header_length, void *header, void *data)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct isight *isight = data;
1698c2ecf20Sopenharmony_ci	const struct audio_payload *payload;
1708c2ecf20Sopenharmony_ci	unsigned int index, length, count, total;
1718c2ecf20Sopenharmony_ci	int err;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (isight->packet_index < 0)
1748c2ecf20Sopenharmony_ci		return;
1758c2ecf20Sopenharmony_ci	index = isight->packet_index;
1768c2ecf20Sopenharmony_ci	payload = isight->buffer.packets[index].buffer;
1778c2ecf20Sopenharmony_ci	length = be32_to_cpup(header) >> 16;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (likely(length >= 16 &&
1808c2ecf20Sopenharmony_ci		   payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) {
1818c2ecf20Sopenharmony_ci		count = be32_to_cpu(payload->sample_count);
1828c2ecf20Sopenharmony_ci		if (likely(count <= (length - 16) / 4)) {
1838c2ecf20Sopenharmony_ci			total = be32_to_cpu(payload->sample_total);
1848c2ecf20Sopenharmony_ci			if (unlikely(total != isight->total_samples)) {
1858c2ecf20Sopenharmony_ci				if (!isight->first_packet)
1868c2ecf20Sopenharmony_ci					isight_dropped_samples(isight, total);
1878c2ecf20Sopenharmony_ci				isight->first_packet = false;
1888c2ecf20Sopenharmony_ci				isight->total_samples = total;
1898c2ecf20Sopenharmony_ci			}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci			isight_samples(isight, payload->samples, count);
1928c2ecf20Sopenharmony_ci			isight->total_samples += count;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	err = fw_iso_context_queue(isight->context, &audio_packet,
1978c2ecf20Sopenharmony_ci				   &isight->buffer.iso_buffer,
1988c2ecf20Sopenharmony_ci				   isight->buffer.packets[index].offset);
1998c2ecf20Sopenharmony_ci	if (err < 0) {
2008c2ecf20Sopenharmony_ci		dev_err(&isight->unit->device, "queueing error: %d\n", err);
2018c2ecf20Sopenharmony_ci		isight_pcm_abort(isight);
2028c2ecf20Sopenharmony_ci		isight->packet_index = -1;
2038c2ecf20Sopenharmony_ci		return;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci	fw_iso_context_queue_flush(isight->context);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (++index >= QUEUE_LENGTH)
2088c2ecf20Sopenharmony_ci		index = 0;
2098c2ecf20Sopenharmony_ci	isight->packet_index = index;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int isight_connect(struct isight *isight)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	int ch, err;
2158c2ecf20Sopenharmony_ci	__be32 value;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ciretry_after_bus_reset:
2188c2ecf20Sopenharmony_ci	ch = fw_iso_resources_allocate(&isight->resources,
2198c2ecf20Sopenharmony_ci				       sizeof(struct audio_payload),
2208c2ecf20Sopenharmony_ci				       isight->device->max_speed);
2218c2ecf20Sopenharmony_ci	if (ch < 0) {
2228c2ecf20Sopenharmony_ci		err = ch;
2238c2ecf20Sopenharmony_ci		goto error;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
2278c2ecf20Sopenharmony_ci	err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
2288c2ecf20Sopenharmony_ci				 isight->audio_base + REG_ISO_TX_CONFIG,
2298c2ecf20Sopenharmony_ci				 &value, 4, FW_FIXED_GENERATION |
2308c2ecf20Sopenharmony_ci				 isight->resources.generation);
2318c2ecf20Sopenharmony_ci	if (err == -EAGAIN) {
2328c2ecf20Sopenharmony_ci		fw_iso_resources_free(&isight->resources);
2338c2ecf20Sopenharmony_ci		goto retry_after_bus_reset;
2348c2ecf20Sopenharmony_ci	} else if (err < 0) {
2358c2ecf20Sopenharmony_ci		goto err_resources;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return 0;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cierr_resources:
2418c2ecf20Sopenharmony_ci	fw_iso_resources_free(&isight->resources);
2428c2ecf20Sopenharmony_cierror:
2438c2ecf20Sopenharmony_ci	return err;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int isight_open(struct snd_pcm_substream *substream)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	static const struct snd_pcm_hardware hardware = {
2498c2ecf20Sopenharmony_ci		.info = SNDRV_PCM_INFO_MMAP |
2508c2ecf20Sopenharmony_ci			SNDRV_PCM_INFO_MMAP_VALID |
2518c2ecf20Sopenharmony_ci			SNDRV_PCM_INFO_BATCH |
2528c2ecf20Sopenharmony_ci			SNDRV_PCM_INFO_INTERLEAVED |
2538c2ecf20Sopenharmony_ci			SNDRV_PCM_INFO_BLOCK_TRANSFER,
2548c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S16_BE,
2558c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_48000,
2568c2ecf20Sopenharmony_ci		.rate_min = 48000,
2578c2ecf20Sopenharmony_ci		.rate_max = 48000,
2588c2ecf20Sopenharmony_ci		.channels_min = 2,
2598c2ecf20Sopenharmony_ci		.channels_max = 2,
2608c2ecf20Sopenharmony_ci		.buffer_bytes_max = 4 * 1024 * 1024,
2618c2ecf20Sopenharmony_ci		.period_bytes_min = MAX_FRAMES_PER_PACKET * 4,
2628c2ecf20Sopenharmony_ci		.period_bytes_max = 1024 * 1024,
2638c2ecf20Sopenharmony_ci		.periods_min = 2,
2648c2ecf20Sopenharmony_ci		.periods_max = UINT_MAX,
2658c2ecf20Sopenharmony_ci	};
2668c2ecf20Sopenharmony_ci	struct isight *isight = substream->private_data;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	substream->runtime->hw = hardware;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return iso_packets_buffer_init(&isight->buffer, isight->unit,
2718c2ecf20Sopenharmony_ci				       QUEUE_LENGTH,
2728c2ecf20Sopenharmony_ci				       sizeof(struct audio_payload),
2738c2ecf20Sopenharmony_ci				       DMA_FROM_DEVICE);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int isight_close(struct snd_pcm_substream *substream)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct isight *isight = substream->private_data;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	iso_packets_buffer_destroy(&isight->buffer, isight->unit);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return 0;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int isight_hw_params(struct snd_pcm_substream *substream,
2868c2ecf20Sopenharmony_ci			    struct snd_pcm_hw_params *hw_params)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct isight *isight = substream->private_data;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	WRITE_ONCE(isight->pcm_active, true);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return 0;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int reg_read(struct isight *isight, int offset, __be32 *value)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
2988c2ecf20Sopenharmony_ci				  isight->audio_base + offset, value, 4, 0);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int reg_write(struct isight *isight, int offset, __be32 value)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
3048c2ecf20Sopenharmony_ci				  isight->audio_base + offset, &value, 4, 0);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void isight_stop_streaming(struct isight *isight)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	__be32 value;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (!isight->context)
3128c2ecf20Sopenharmony_ci		return;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	fw_iso_context_stop(isight->context);
3158c2ecf20Sopenharmony_ci	fw_iso_context_destroy(isight->context);
3168c2ecf20Sopenharmony_ci	isight->context = NULL;
3178c2ecf20Sopenharmony_ci	fw_iso_resources_free(&isight->resources);
3188c2ecf20Sopenharmony_ci	value = 0;
3198c2ecf20Sopenharmony_ci	snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
3208c2ecf20Sopenharmony_ci			   isight->audio_base + REG_AUDIO_ENABLE,
3218c2ecf20Sopenharmony_ci			   &value, 4, FW_QUIET);
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic int isight_hw_free(struct snd_pcm_substream *substream)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct isight *isight = substream->private_data;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	WRITE_ONCE(isight->pcm_active, false);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	mutex_lock(&isight->mutex);
3318c2ecf20Sopenharmony_ci	isight_stop_streaming(isight);
3328c2ecf20Sopenharmony_ci	mutex_unlock(&isight->mutex);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int isight_start_streaming(struct isight *isight)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	unsigned int i;
3408c2ecf20Sopenharmony_ci	int err;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (isight->context) {
3438c2ecf20Sopenharmony_ci		if (isight->packet_index < 0)
3448c2ecf20Sopenharmony_ci			isight_stop_streaming(isight);
3458c2ecf20Sopenharmony_ci		else
3468c2ecf20Sopenharmony_ci			return 0;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000));
3508c2ecf20Sopenharmony_ci	if (err < 0)
3518c2ecf20Sopenharmony_ci		goto error;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	err = isight_connect(isight);
3548c2ecf20Sopenharmony_ci	if (err < 0)
3558c2ecf20Sopenharmony_ci		goto error;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE));
3588c2ecf20Sopenharmony_ci	if (err < 0)
3598c2ecf20Sopenharmony_ci		goto err_resources;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	isight->context = fw_iso_context_create(isight->device->card,
3628c2ecf20Sopenharmony_ci						FW_ISO_CONTEXT_RECEIVE,
3638c2ecf20Sopenharmony_ci						isight->resources.channel,
3648c2ecf20Sopenharmony_ci						isight->device->max_speed,
3658c2ecf20Sopenharmony_ci						4, isight_packet, isight);
3668c2ecf20Sopenharmony_ci	if (IS_ERR(isight->context)) {
3678c2ecf20Sopenharmony_ci		err = PTR_ERR(isight->context);
3688c2ecf20Sopenharmony_ci		isight->context = NULL;
3698c2ecf20Sopenharmony_ci		goto err_resources;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	for (i = 0; i < QUEUE_LENGTH; ++i) {
3738c2ecf20Sopenharmony_ci		err = fw_iso_context_queue(isight->context, &audio_packet,
3748c2ecf20Sopenharmony_ci					   &isight->buffer.iso_buffer,
3758c2ecf20Sopenharmony_ci					   isight->buffer.packets[i].offset);
3768c2ecf20Sopenharmony_ci		if (err < 0)
3778c2ecf20Sopenharmony_ci			goto err_context;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	isight->first_packet = true;
3818c2ecf20Sopenharmony_ci	isight->packet_index = 0;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	err = fw_iso_context_start(isight->context, -1, 0,
3848c2ecf20Sopenharmony_ci				   FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/);
3858c2ecf20Sopenharmony_ci	if (err < 0)
3868c2ecf20Sopenharmony_ci		goto err_context;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cierr_context:
3918c2ecf20Sopenharmony_ci	fw_iso_context_destroy(isight->context);
3928c2ecf20Sopenharmony_ci	isight->context = NULL;
3938c2ecf20Sopenharmony_cierr_resources:
3948c2ecf20Sopenharmony_ci	fw_iso_resources_free(&isight->resources);
3958c2ecf20Sopenharmony_ci	reg_write(isight, REG_AUDIO_ENABLE, 0);
3968c2ecf20Sopenharmony_cierror:
3978c2ecf20Sopenharmony_ci	return err;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic int isight_prepare(struct snd_pcm_substream *substream)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	struct isight *isight = substream->private_data;
4038c2ecf20Sopenharmony_ci	int err;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	isight->buffer_pointer = 0;
4068c2ecf20Sopenharmony_ci	isight->period_counter = 0;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	mutex_lock(&isight->mutex);
4098c2ecf20Sopenharmony_ci	err = isight_start_streaming(isight);
4108c2ecf20Sopenharmony_ci	mutex_unlock(&isight->mutex);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return err;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int isight_trigger(struct snd_pcm_substream *substream, int cmd)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct isight *isight = substream->private_data;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	switch (cmd) {
4208c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
4218c2ecf20Sopenharmony_ci		WRITE_ONCE(isight->pcm_running, true);
4228c2ecf20Sopenharmony_ci		break;
4238c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
4248c2ecf20Sopenharmony_ci		WRITE_ONCE(isight->pcm_running, false);
4258c2ecf20Sopenharmony_ci		break;
4268c2ecf20Sopenharmony_ci	default:
4278c2ecf20Sopenharmony_ci		return -EINVAL;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct isight *isight = substream->private_data;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	return READ_ONCE(isight->buffer_pointer);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic int isight_create_pcm(struct isight *isight)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	static const struct snd_pcm_ops ops = {
4428c2ecf20Sopenharmony_ci		.open      = isight_open,
4438c2ecf20Sopenharmony_ci		.close     = isight_close,
4448c2ecf20Sopenharmony_ci		.hw_params = isight_hw_params,
4458c2ecf20Sopenharmony_ci		.hw_free   = isight_hw_free,
4468c2ecf20Sopenharmony_ci		.prepare   = isight_prepare,
4478c2ecf20Sopenharmony_ci		.trigger   = isight_trigger,
4488c2ecf20Sopenharmony_ci		.pointer   = isight_pointer,
4498c2ecf20Sopenharmony_ci	};
4508c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
4518c2ecf20Sopenharmony_ci	int err;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm);
4548c2ecf20Sopenharmony_ci	if (err < 0)
4558c2ecf20Sopenharmony_ci		return err;
4568c2ecf20Sopenharmony_ci	pcm->private_data = isight;
4578c2ecf20Sopenharmony_ci	strcpy(pcm->name, "iSight");
4588c2ecf20Sopenharmony_ci	isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
4598c2ecf20Sopenharmony_ci	isight->pcm->ops = &ops;
4608c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int isight_gain_info(struct snd_kcontrol *ctl,
4668c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_info *info)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct isight *isight = ctl->private_data;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4718c2ecf20Sopenharmony_ci	info->count = 1;
4728c2ecf20Sopenharmony_ci	info->value.integer.min = isight->gain_min;
4738c2ecf20Sopenharmony_ci	info->value.integer.max = isight->gain_max;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return 0;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic int isight_gain_get(struct snd_kcontrol *ctl,
4798c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_value *value)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	struct isight *isight = ctl->private_data;
4828c2ecf20Sopenharmony_ci	__be32 gain;
4838c2ecf20Sopenharmony_ci	int err;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	err = reg_read(isight, REG_GAIN, &gain);
4868c2ecf20Sopenharmony_ci	if (err < 0)
4878c2ecf20Sopenharmony_ci		return err;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	value->value.integer.value[0] = (s32)be32_to_cpu(gain);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	return 0;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic int isight_gain_put(struct snd_kcontrol *ctl,
4958c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_value *value)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	struct isight *isight = ctl->private_data;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (value->value.integer.value[0] < isight->gain_min ||
5008c2ecf20Sopenharmony_ci	    value->value.integer.value[0] > isight->gain_max)
5018c2ecf20Sopenharmony_ci		return -EINVAL;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	return reg_write(isight, REG_GAIN,
5048c2ecf20Sopenharmony_ci			 cpu_to_be32(value->value.integer.value[0]));
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic int isight_mute_get(struct snd_kcontrol *ctl,
5088c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_value *value)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct isight *isight = ctl->private_data;
5118c2ecf20Sopenharmony_ci	__be32 mute;
5128c2ecf20Sopenharmony_ci	int err;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	err = reg_read(isight, REG_MUTE, &mute);
5158c2ecf20Sopenharmony_ci	if (err < 0)
5168c2ecf20Sopenharmony_ci		return err;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	value->value.integer.value[0] = !mute;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	return 0;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic int isight_mute_put(struct snd_kcontrol *ctl,
5248c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_value *value)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	struct isight *isight = ctl->private_data;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	return reg_write(isight, REG_MUTE,
5298c2ecf20Sopenharmony_ci			 (__force __be32)!value->value.integer.value[0]);
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic int isight_create_mixer(struct isight *isight)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	static const struct snd_kcontrol_new gain_control = {
5358c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5368c2ecf20Sopenharmony_ci		.name = "Mic Capture Volume",
5378c2ecf20Sopenharmony_ci		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
5388c2ecf20Sopenharmony_ci			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
5398c2ecf20Sopenharmony_ci		.info = isight_gain_info,
5408c2ecf20Sopenharmony_ci		.get = isight_gain_get,
5418c2ecf20Sopenharmony_ci		.put = isight_gain_put,
5428c2ecf20Sopenharmony_ci	};
5438c2ecf20Sopenharmony_ci	static const struct snd_kcontrol_new mute_control = {
5448c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5458c2ecf20Sopenharmony_ci		.name = "Mic Capture Switch",
5468c2ecf20Sopenharmony_ci		.info = snd_ctl_boolean_mono_info,
5478c2ecf20Sopenharmony_ci		.get = isight_mute_get,
5488c2ecf20Sopenharmony_ci		.put = isight_mute_put,
5498c2ecf20Sopenharmony_ci	};
5508c2ecf20Sopenharmony_ci	__be32 value;
5518c2ecf20Sopenharmony_ci	struct snd_kcontrol *ctl;
5528c2ecf20Sopenharmony_ci	int err;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	err = reg_read(isight, REG_GAIN_RAW_START, &value);
5558c2ecf20Sopenharmony_ci	if (err < 0)
5568c2ecf20Sopenharmony_ci		return err;
5578c2ecf20Sopenharmony_ci	isight->gain_min = be32_to_cpu(value);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	err = reg_read(isight, REG_GAIN_RAW_END, &value);
5608c2ecf20Sopenharmony_ci	if (err < 0)
5618c2ecf20Sopenharmony_ci		return err;
5628c2ecf20Sopenharmony_ci	isight->gain_max = be32_to_cpu(value);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	isight->gain_tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_MINMAX;
5658c2ecf20Sopenharmony_ci	isight->gain_tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	err = reg_read(isight, REG_GAIN_DB_START, &value);
5688c2ecf20Sopenharmony_ci	if (err < 0)
5698c2ecf20Sopenharmony_ci		return err;
5708c2ecf20Sopenharmony_ci	isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MIN] =
5718c2ecf20Sopenharmony_ci						(s32)be32_to_cpu(value) * 100;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	err = reg_read(isight, REG_GAIN_DB_END, &value);
5748c2ecf20Sopenharmony_ci	if (err < 0)
5758c2ecf20Sopenharmony_ci		return err;
5768c2ecf20Sopenharmony_ci	isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX] =
5778c2ecf20Sopenharmony_ci						(s32)be32_to_cpu(value) * 100;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	ctl = snd_ctl_new1(&gain_control, isight);
5808c2ecf20Sopenharmony_ci	if (ctl)
5818c2ecf20Sopenharmony_ci		ctl->tlv.p = isight->gain_tlv;
5828c2ecf20Sopenharmony_ci	err = snd_ctl_add(isight->card, ctl);
5838c2ecf20Sopenharmony_ci	if (err < 0)
5848c2ecf20Sopenharmony_ci		return err;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight));
5878c2ecf20Sopenharmony_ci	if (err < 0)
5888c2ecf20Sopenharmony_ci		return err;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	return 0;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic void isight_card_free(struct snd_card *card)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	struct isight *isight = card->private_data;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	fw_iso_resources_destroy(&isight->resources);
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic u64 get_unit_base(struct fw_unit *unit)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	struct fw_csr_iterator i;
6038c2ecf20Sopenharmony_ci	int key, value;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	fw_csr_iterator_init(&i, unit->directory);
6068c2ecf20Sopenharmony_ci	while (fw_csr_iterator_next(&i, &key, &value))
6078c2ecf20Sopenharmony_ci		if (key == CSR_OFFSET)
6088c2ecf20Sopenharmony_ci			return CSR_REGISTER_BASE + value * 4;
6098c2ecf20Sopenharmony_ci	return 0;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic int isight_probe(struct fw_unit *unit,
6138c2ecf20Sopenharmony_ci			const struct ieee1394_device_id *id)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	struct fw_device *fw_dev = fw_parent_device(unit);
6168c2ecf20Sopenharmony_ci	struct snd_card *card;
6178c2ecf20Sopenharmony_ci	struct isight *isight;
6188c2ecf20Sopenharmony_ci	int err;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
6218c2ecf20Sopenharmony_ci			   sizeof(*isight), &card);
6228c2ecf20Sopenharmony_ci	if (err < 0)
6238c2ecf20Sopenharmony_ci		return err;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	isight = card->private_data;
6268c2ecf20Sopenharmony_ci	isight->card = card;
6278c2ecf20Sopenharmony_ci	mutex_init(&isight->mutex);
6288c2ecf20Sopenharmony_ci	isight->unit = fw_unit_get(unit);
6298c2ecf20Sopenharmony_ci	isight->device = fw_dev;
6308c2ecf20Sopenharmony_ci	isight->audio_base = get_unit_base(unit);
6318c2ecf20Sopenharmony_ci	if (!isight->audio_base) {
6328c2ecf20Sopenharmony_ci		dev_err(&unit->device, "audio unit base not found\n");
6338c2ecf20Sopenharmony_ci		err = -ENXIO;
6348c2ecf20Sopenharmony_ci		goto error;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci	fw_iso_resources_init(&isight->resources, unit);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	card->private_free = isight_card_free;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	strcpy(card->driver, "iSight");
6418c2ecf20Sopenharmony_ci	strcpy(card->shortname, "Apple iSight");
6428c2ecf20Sopenharmony_ci	snprintf(card->longname, sizeof(card->longname),
6438c2ecf20Sopenharmony_ci		 "Apple iSight (GUID %08x%08x) at %s, S%d",
6448c2ecf20Sopenharmony_ci		 fw_dev->config_rom[3], fw_dev->config_rom[4],
6458c2ecf20Sopenharmony_ci		 dev_name(&unit->device), 100 << fw_dev->max_speed);
6468c2ecf20Sopenharmony_ci	strcpy(card->mixername, "iSight");
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	err = isight_create_pcm(isight);
6498c2ecf20Sopenharmony_ci	if (err < 0)
6508c2ecf20Sopenharmony_ci		goto error;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	err = isight_create_mixer(isight);
6538c2ecf20Sopenharmony_ci	if (err < 0)
6548c2ecf20Sopenharmony_ci		goto error;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	err = snd_card_register(card);
6578c2ecf20Sopenharmony_ci	if (err < 0)
6588c2ecf20Sopenharmony_ci		goto error;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	dev_set_drvdata(&unit->device, isight);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	return 0;
6638c2ecf20Sopenharmony_cierror:
6648c2ecf20Sopenharmony_ci	snd_card_free(card);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	mutex_destroy(&isight->mutex);
6678c2ecf20Sopenharmony_ci	fw_unit_put(isight->unit);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	return err;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic void isight_bus_reset(struct fw_unit *unit)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	struct isight *isight = dev_get_drvdata(&unit->device);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	if (fw_iso_resources_update(&isight->resources) < 0) {
6778c2ecf20Sopenharmony_ci		isight_pcm_abort(isight);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci		mutex_lock(&isight->mutex);
6808c2ecf20Sopenharmony_ci		isight_stop_streaming(isight);
6818c2ecf20Sopenharmony_ci		mutex_unlock(&isight->mutex);
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic void isight_remove(struct fw_unit *unit)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	struct isight *isight = dev_get_drvdata(&unit->device);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	isight_pcm_abort(isight);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	snd_card_disconnect(isight->card);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	mutex_lock(&isight->mutex);
6948c2ecf20Sopenharmony_ci	isight_stop_streaming(isight);
6958c2ecf20Sopenharmony_ci	mutex_unlock(&isight->mutex);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	// Block till all of ALSA character devices are released.
6988c2ecf20Sopenharmony_ci	snd_card_free(isight->card);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	mutex_destroy(&isight->mutex);
7018c2ecf20Sopenharmony_ci	fw_unit_put(isight->unit);
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic const struct ieee1394_device_id isight_id_table[] = {
7058c2ecf20Sopenharmony_ci	{
7068c2ecf20Sopenharmony_ci		.match_flags  = IEEE1394_MATCH_SPECIFIER_ID |
7078c2ecf20Sopenharmony_ci				IEEE1394_MATCH_VERSION,
7088c2ecf20Sopenharmony_ci		.specifier_id = OUI_APPLE,
7098c2ecf20Sopenharmony_ci		.version      = SW_ISIGHT_AUDIO,
7108c2ecf20Sopenharmony_ci	},
7118c2ecf20Sopenharmony_ci	{ }
7128c2ecf20Sopenharmony_ci};
7138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ieee1394, isight_id_table);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic struct fw_driver isight_driver = {
7168c2ecf20Sopenharmony_ci	.driver   = {
7178c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
7188c2ecf20Sopenharmony_ci		.name	= KBUILD_MODNAME,
7198c2ecf20Sopenharmony_ci		.bus	= &fw_bus_type,
7208c2ecf20Sopenharmony_ci	},
7218c2ecf20Sopenharmony_ci	.probe    = isight_probe,
7228c2ecf20Sopenharmony_ci	.update   = isight_bus_reset,
7238c2ecf20Sopenharmony_ci	.remove   = isight_remove,
7248c2ecf20Sopenharmony_ci	.id_table = isight_id_table,
7258c2ecf20Sopenharmony_ci};
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cistatic int __init alsa_isight_init(void)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	return driver_register(&isight_driver.driver);
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cistatic void __exit alsa_isight_exit(void)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	driver_unregister(&isight_driver.driver);
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cimodule_init(alsa_isight_init);
7388c2ecf20Sopenharmony_cimodule_exit(alsa_isight_exit);
739