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