13a691b28SClemens Ladisch /* 23a691b28SClemens Ladisch * Apple iSight audio driver 33a691b28SClemens Ladisch * 43a691b28SClemens Ladisch * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 53a691b28SClemens Ladisch * Licensed under the terms of the GNU General Public License, version 2. 63a691b28SClemens Ladisch */ 73a691b28SClemens Ladisch 8ac34dad2SStefan Richter #include <asm/byteorder.h> 93a691b28SClemens Ladisch #include <linux/delay.h> 103a691b28SClemens Ladisch #include <linux/device.h> 113a691b28SClemens Ladisch #include <linux/firewire.h> 123a691b28SClemens Ladisch #include <linux/firewire-constants.h> 133a691b28SClemens Ladisch #include <linux/module.h> 143a691b28SClemens Ladisch #include <linux/mod_devicetable.h> 153a691b28SClemens Ladisch #include <linux/mutex.h> 163a691b28SClemens Ladisch #include <linux/string.h> 173a691b28SClemens Ladisch #include <sound/control.h> 183a691b28SClemens Ladisch #include <sound/core.h> 193a691b28SClemens Ladisch #include <sound/initval.h> 203a691b28SClemens Ladisch #include <sound/pcm.h> 213a691b28SClemens Ladisch #include <sound/tlv.h> 223a691b28SClemens Ladisch #include "lib.h" 233a691b28SClemens Ladisch #include "iso-resources.h" 243a691b28SClemens Ladisch #include "packets-buffer.h" 253a691b28SClemens Ladisch 263a691b28SClemens Ladisch #define OUI_APPLE 0x000a27 273a691b28SClemens Ladisch #define MODEL_APPLE_ISIGHT 0x000008 283a691b28SClemens Ladisch #define SW_ISIGHT_AUDIO 0x000010 293a691b28SClemens Ladisch 303a691b28SClemens Ladisch #define REG_AUDIO_ENABLE 0x000 313a691b28SClemens Ladisch #define AUDIO_ENABLE 0x80000000 323a691b28SClemens Ladisch #define REG_DEF_AUDIO_GAIN 0x204 333a691b28SClemens Ladisch #define REG_GAIN_RAW_START 0x210 343a691b28SClemens Ladisch #define REG_GAIN_RAW_END 0x214 353a691b28SClemens Ladisch #define REG_GAIN_DB_START 0x218 363a691b28SClemens Ladisch #define REG_GAIN_DB_END 0x21c 373a691b28SClemens Ladisch #define REG_SAMPLE_RATE_INQUIRY 0x280 383a691b28SClemens Ladisch #define REG_ISO_TX_CONFIG 0x300 393a691b28SClemens Ladisch #define SPEED_SHIFT 16 403a691b28SClemens Ladisch #define REG_SAMPLE_RATE 0x400 413a691b28SClemens Ladisch #define RATE_48000 0x80000000 423a691b28SClemens Ladisch #define REG_GAIN 0x500 433a691b28SClemens Ladisch #define REG_MUTE 0x504 443a691b28SClemens Ladisch 453a691b28SClemens Ladisch #define MAX_FRAMES_PER_PACKET 475 463a691b28SClemens Ladisch 473a691b28SClemens Ladisch #define QUEUE_LENGTH 20 483a691b28SClemens Ladisch 493a691b28SClemens Ladisch struct isight { 503a691b28SClemens Ladisch struct snd_card *card; 513a691b28SClemens Ladisch struct fw_unit *unit; 523a691b28SClemens Ladisch struct fw_device *device; 533a691b28SClemens Ladisch u64 audio_base; 543a691b28SClemens Ladisch struct snd_pcm_substream *pcm; 553a691b28SClemens Ladisch struct mutex mutex; 563a691b28SClemens Ladisch struct iso_packets_buffer buffer; 573a691b28SClemens Ladisch struct fw_iso_resources resources; 583a691b28SClemens Ladisch struct fw_iso_context *context; 5903c29680SClemens Ladisch bool pcm_active; 603a691b28SClemens Ladisch bool pcm_running; 613a691b28SClemens Ladisch bool first_packet; 623a691b28SClemens Ladisch int packet_index; 633a691b28SClemens Ladisch u32 total_samples; 643a691b28SClemens Ladisch unsigned int buffer_pointer; 653a691b28SClemens Ladisch unsigned int period_counter; 663a691b28SClemens Ladisch s32 gain_min, gain_max; 673a691b28SClemens Ladisch unsigned int gain_tlv[4]; 683a691b28SClemens Ladisch }; 693a691b28SClemens Ladisch 703a691b28SClemens Ladisch struct audio_payload { 713a691b28SClemens Ladisch __be32 sample_count; 723a691b28SClemens Ladisch __be32 signature; 733a691b28SClemens Ladisch __be32 sample_total; 743a691b28SClemens Ladisch __be32 reserved; 753a691b28SClemens Ladisch __be16 samples[2 * MAX_FRAMES_PER_PACKET]; 763a691b28SClemens Ladisch }; 773a691b28SClemens Ladisch 783a691b28SClemens Ladisch MODULE_DESCRIPTION("iSight audio driver"); 793a691b28SClemens Ladisch MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); 803a691b28SClemens Ladisch MODULE_LICENSE("GPL v2"); 813a691b28SClemens Ladisch 823a691b28SClemens Ladisch static struct fw_iso_packet audio_packet = { 833a691b28SClemens Ladisch .payload_length = sizeof(struct audio_payload), 843a691b28SClemens Ladisch .interrupt = 1, 85f2934cd4SClemens Ladisch .header_length = 4, 863a691b28SClemens Ladisch }; 873a691b28SClemens Ladisch 883a691b28SClemens Ladisch static void isight_update_pointers(struct isight *isight, unsigned int count) 893a691b28SClemens Ladisch { 903a691b28SClemens Ladisch struct snd_pcm_runtime *runtime = isight->pcm->runtime; 913a691b28SClemens Ladisch unsigned int ptr; 923a691b28SClemens Ladisch 933a691b28SClemens Ladisch smp_wmb(); /* update buffer data before buffer pointer */ 943a691b28SClemens Ladisch 953a691b28SClemens Ladisch ptr = isight->buffer_pointer; 963a691b28SClemens Ladisch ptr += count; 973a691b28SClemens Ladisch if (ptr >= runtime->buffer_size) 983a691b28SClemens Ladisch ptr -= runtime->buffer_size; 993a691b28SClemens Ladisch ACCESS_ONCE(isight->buffer_pointer) = ptr; 1003a691b28SClemens Ladisch 1013a691b28SClemens Ladisch isight->period_counter += count; 1023a691b28SClemens Ladisch if (isight->period_counter >= runtime->period_size) { 1033a691b28SClemens Ladisch isight->period_counter -= runtime->period_size; 1043a691b28SClemens Ladisch snd_pcm_period_elapsed(isight->pcm); 1053a691b28SClemens Ladisch } 1063a691b28SClemens Ladisch } 1073a691b28SClemens Ladisch 1083a691b28SClemens Ladisch static void isight_samples(struct isight *isight, 1093a691b28SClemens Ladisch const __be16 *samples, unsigned int count) 1103a691b28SClemens Ladisch { 1113a691b28SClemens Ladisch struct snd_pcm_runtime *runtime; 1123a691b28SClemens Ladisch unsigned int count1; 1133a691b28SClemens Ladisch 1143a691b28SClemens Ladisch if (!ACCESS_ONCE(isight->pcm_running)) 1153a691b28SClemens Ladisch return; 1163a691b28SClemens Ladisch 1173a691b28SClemens Ladisch runtime = isight->pcm->runtime; 1183a691b28SClemens Ladisch if (isight->buffer_pointer + count <= runtime->buffer_size) { 1193a691b28SClemens Ladisch memcpy(runtime->dma_area + isight->buffer_pointer * 4, 1203a691b28SClemens Ladisch samples, count * 4); 1213a691b28SClemens Ladisch } else { 1223a691b28SClemens Ladisch count1 = runtime->buffer_size - isight->buffer_pointer; 1233a691b28SClemens Ladisch memcpy(runtime->dma_area + isight->buffer_pointer * 4, 1243a691b28SClemens Ladisch samples, count1 * 4); 1253a691b28SClemens Ladisch samples += count1 * 2; 1263a691b28SClemens Ladisch memcpy(runtime->dma_area, samples, (count - count1) * 4); 1273a691b28SClemens Ladisch } 1283a691b28SClemens Ladisch 1293a691b28SClemens Ladisch isight_update_pointers(isight, count); 1303a691b28SClemens Ladisch } 1313a691b28SClemens Ladisch 1323a691b28SClemens Ladisch static void isight_pcm_abort(struct isight *isight) 1333a691b28SClemens Ladisch { 1343a691b28SClemens Ladisch unsigned long flags; 1353a691b28SClemens Ladisch 13603c29680SClemens Ladisch if (ACCESS_ONCE(isight->pcm_active)) { 1373a691b28SClemens Ladisch snd_pcm_stream_lock_irqsave(isight->pcm, flags); 1383a691b28SClemens Ladisch if (snd_pcm_running(isight->pcm)) 1393a691b28SClemens Ladisch snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN); 1403a691b28SClemens Ladisch snd_pcm_stream_unlock_irqrestore(isight->pcm, flags); 1413a691b28SClemens Ladisch } 14203c29680SClemens Ladisch } 1433a691b28SClemens Ladisch 1443a691b28SClemens Ladisch static void isight_dropped_samples(struct isight *isight, unsigned int total) 1453a691b28SClemens Ladisch { 1463a691b28SClemens Ladisch struct snd_pcm_runtime *runtime; 1473a691b28SClemens Ladisch u32 dropped; 1483a691b28SClemens Ladisch unsigned int count1; 1493a691b28SClemens Ladisch 1503a691b28SClemens Ladisch if (!ACCESS_ONCE(isight->pcm_running)) 1513a691b28SClemens Ladisch return; 1523a691b28SClemens Ladisch 1533a691b28SClemens Ladisch runtime = isight->pcm->runtime; 1543a691b28SClemens Ladisch dropped = total - isight->total_samples; 1553a691b28SClemens Ladisch if (dropped < runtime->buffer_size) { 1563a691b28SClemens Ladisch if (isight->buffer_pointer + dropped <= runtime->buffer_size) { 1573a691b28SClemens Ladisch memset(runtime->dma_area + isight->buffer_pointer * 4, 1583a691b28SClemens Ladisch 0, dropped * 4); 1593a691b28SClemens Ladisch } else { 1603a691b28SClemens Ladisch count1 = runtime->buffer_size - isight->buffer_pointer; 1613a691b28SClemens Ladisch memset(runtime->dma_area + isight->buffer_pointer * 4, 1623a691b28SClemens Ladisch 0, count1 * 4); 1633a691b28SClemens Ladisch memset(runtime->dma_area, 0, (dropped - count1) * 4); 1643a691b28SClemens Ladisch } 1653a691b28SClemens Ladisch isight_update_pointers(isight, dropped); 1663a691b28SClemens Ladisch } else { 1673a691b28SClemens Ladisch isight_pcm_abort(isight); 1683a691b28SClemens Ladisch } 1693a691b28SClemens Ladisch } 1703a691b28SClemens Ladisch 1713a691b28SClemens Ladisch static void isight_packet(struct fw_iso_context *context, u32 cycle, 1723a691b28SClemens Ladisch size_t header_length, void *header, void *data) 1733a691b28SClemens Ladisch { 1743a691b28SClemens Ladisch struct isight *isight = data; 1753a691b28SClemens Ladisch const struct audio_payload *payload; 1763a691b28SClemens Ladisch unsigned int index, length, count, total; 1773a691b28SClemens Ladisch int err; 1783a691b28SClemens Ladisch 1793a691b28SClemens Ladisch if (isight->packet_index < 0) 1803a691b28SClemens Ladisch return; 1813a691b28SClemens Ladisch index = isight->packet_index; 1823a691b28SClemens Ladisch payload = isight->buffer.packets[index].buffer; 1833a691b28SClemens Ladisch length = be32_to_cpup(header) >> 16; 1843a691b28SClemens Ladisch 1853a691b28SClemens Ladisch if (likely(length >= 16 && 1863a691b28SClemens Ladisch payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) { 1873a691b28SClemens Ladisch count = be32_to_cpu(payload->sample_count); 1883a691b28SClemens Ladisch if (likely(count <= (length - 16) / 4)) { 1893a691b28SClemens Ladisch total = be32_to_cpu(payload->sample_total); 1903a691b28SClemens Ladisch if (unlikely(total != isight->total_samples)) { 1913a691b28SClemens Ladisch if (!isight->first_packet) 1923a691b28SClemens Ladisch isight_dropped_samples(isight, total); 1933a691b28SClemens Ladisch isight->first_packet = false; 1943a691b28SClemens Ladisch isight->total_samples = total; 1953a691b28SClemens Ladisch } 1963a691b28SClemens Ladisch 1973a691b28SClemens Ladisch isight_samples(isight, payload->samples, count); 1983a691b28SClemens Ladisch isight->total_samples += count; 1993a691b28SClemens Ladisch } 2003a691b28SClemens Ladisch } 2013a691b28SClemens Ladisch 2023a691b28SClemens Ladisch err = fw_iso_context_queue(isight->context, &audio_packet, 2033a691b28SClemens Ladisch &isight->buffer.iso_buffer, 2043a691b28SClemens Ladisch isight->buffer.packets[index].offset); 2053a691b28SClemens Ladisch if (err < 0) { 2063a691b28SClemens Ladisch dev_err(&isight->unit->device, "queueing error: %d\n", err); 2073a691b28SClemens Ladisch isight_pcm_abort(isight); 2083a691b28SClemens Ladisch isight->packet_index = -1; 2093a691b28SClemens Ladisch return; 2103a691b28SClemens Ladisch } 211cf6f1ff1SClemens Ladisch fw_iso_context_queue_flush(isight->context); 2123a691b28SClemens Ladisch 213898732d1SClemens Ladisch if (++index >= QUEUE_LENGTH) 214898732d1SClemens Ladisch index = 0; 2153a691b28SClemens Ladisch isight->packet_index = index; 2163a691b28SClemens Ladisch } 2173a691b28SClemens Ladisch 2183a691b28SClemens Ladisch static int isight_connect(struct isight *isight) 2193a691b28SClemens Ladisch { 2203a691b28SClemens Ladisch int ch, err, rcode, errors = 0; 2213a691b28SClemens Ladisch __be32 value; 2223a691b28SClemens Ladisch 2233a691b28SClemens Ladisch retry_after_bus_reset: 2243a691b28SClemens Ladisch ch = fw_iso_resources_allocate(&isight->resources, 2253a691b28SClemens Ladisch sizeof(struct audio_payload), 2263a691b28SClemens Ladisch isight->device->max_speed); 2273a691b28SClemens Ladisch if (ch < 0) { 2283a691b28SClemens Ladisch err = ch; 2293a691b28SClemens Ladisch goto error; 2303a691b28SClemens Ladisch } 2313a691b28SClemens Ladisch 2323a691b28SClemens Ladisch value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT)); 2333a691b28SClemens Ladisch for (;;) { 2343a691b28SClemens Ladisch rcode = fw_run_transaction( 2353a691b28SClemens Ladisch isight->device->card, 2363a691b28SClemens Ladisch TCODE_WRITE_QUADLET_REQUEST, 2373a691b28SClemens Ladisch isight->device->node_id, 2383a691b28SClemens Ladisch isight->resources.generation, 2393a691b28SClemens Ladisch isight->device->max_speed, 2403a691b28SClemens Ladisch isight->audio_base + REG_ISO_TX_CONFIG, 2413a691b28SClemens Ladisch &value, 4); 2423a691b28SClemens Ladisch if (rcode == RCODE_COMPLETE) { 2433a691b28SClemens Ladisch return 0; 2443a691b28SClemens Ladisch } else if (rcode == RCODE_GENERATION) { 2453a691b28SClemens Ladisch fw_iso_resources_free(&isight->resources); 2463a691b28SClemens Ladisch goto retry_after_bus_reset; 2473a691b28SClemens Ladisch } else if (rcode_is_permanent_error(rcode) || ++errors >= 3) { 2483a691b28SClemens Ladisch err = -EIO; 2493a691b28SClemens Ladisch goto err_resources; 2503a691b28SClemens Ladisch } 2513a691b28SClemens Ladisch msleep(5); 2523a691b28SClemens Ladisch } 2533a691b28SClemens Ladisch 2543a691b28SClemens Ladisch err_resources: 2553a691b28SClemens Ladisch fw_iso_resources_free(&isight->resources); 2563a691b28SClemens Ladisch error: 2573a691b28SClemens Ladisch return err; 2583a691b28SClemens Ladisch } 2593a691b28SClemens Ladisch 2603a691b28SClemens Ladisch static int isight_open(struct snd_pcm_substream *substream) 2613a691b28SClemens Ladisch { 2623a691b28SClemens Ladisch static const struct snd_pcm_hardware hardware = { 2633a691b28SClemens Ladisch .info = SNDRV_PCM_INFO_MMAP | 2643a691b28SClemens Ladisch SNDRV_PCM_INFO_MMAP_VALID | 2653a691b28SClemens Ladisch SNDRV_PCM_INFO_BATCH | 2663a691b28SClemens Ladisch SNDRV_PCM_INFO_INTERLEAVED | 2673a691b28SClemens Ladisch SNDRV_PCM_INFO_BLOCK_TRANSFER, 2683a691b28SClemens Ladisch .formats = SNDRV_PCM_FMTBIT_S16_BE, 2693a691b28SClemens Ladisch .rates = SNDRV_PCM_RATE_48000, 2703a691b28SClemens Ladisch .rate_min = 48000, 2713a691b28SClemens Ladisch .rate_max = 48000, 2723a691b28SClemens Ladisch .channels_min = 2, 2733a691b28SClemens Ladisch .channels_max = 2, 2743a691b28SClemens Ladisch .buffer_bytes_max = 4 * 1024 * 1024, 2753a691b28SClemens Ladisch .period_bytes_min = MAX_FRAMES_PER_PACKET * 4, 2763a691b28SClemens Ladisch .period_bytes_max = 1024 * 1024, 2773a691b28SClemens Ladisch .periods_min = 2, 2783a691b28SClemens Ladisch .periods_max = UINT_MAX, 2793a691b28SClemens Ladisch }; 2803a691b28SClemens Ladisch struct isight *isight = substream->private_data; 2813a691b28SClemens Ladisch 2823a691b28SClemens Ladisch substream->runtime->hw = hardware; 2833a691b28SClemens Ladisch 2843a691b28SClemens Ladisch return iso_packets_buffer_init(&isight->buffer, isight->unit, 2853a691b28SClemens Ladisch QUEUE_LENGTH, 2863a691b28SClemens Ladisch sizeof(struct audio_payload), 2873a691b28SClemens Ladisch DMA_FROM_DEVICE); 2883a691b28SClemens Ladisch } 2893a691b28SClemens Ladisch 2903a691b28SClemens Ladisch static int isight_close(struct snd_pcm_substream *substream) 2913a691b28SClemens Ladisch { 2923a691b28SClemens Ladisch struct isight *isight = substream->private_data; 2933a691b28SClemens Ladisch 2943a691b28SClemens Ladisch iso_packets_buffer_destroy(&isight->buffer, isight->unit); 2953a691b28SClemens Ladisch 2963a691b28SClemens Ladisch return 0; 2973a691b28SClemens Ladisch } 2983a691b28SClemens Ladisch 2993a691b28SClemens Ladisch static int isight_hw_params(struct snd_pcm_substream *substream, 3003a691b28SClemens Ladisch struct snd_pcm_hw_params *hw_params) 3013a691b28SClemens Ladisch { 30203c29680SClemens Ladisch struct isight *isight = substream->private_data; 30303c29680SClemens Ladisch int err; 30403c29680SClemens Ladisch 30503c29680SClemens Ladisch err = snd_pcm_lib_alloc_vmalloc_buffer(substream, 3063a691b28SClemens Ladisch params_buffer_bytes(hw_params)); 30703c29680SClemens Ladisch if (err < 0) 30803c29680SClemens Ladisch return err; 30903c29680SClemens Ladisch 31003c29680SClemens Ladisch ACCESS_ONCE(isight->pcm_active) = true; 31103c29680SClemens Ladisch 31203c29680SClemens Ladisch return 0; 3133a691b28SClemens Ladisch } 3143a691b28SClemens Ladisch 315ac34dad2SStefan Richter static int reg_read(struct isight *isight, int offset, __be32 *value) 316ac34dad2SStefan Richter { 317ac34dad2SStefan Richter return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, 318ac34dad2SStefan Richter isight->audio_base + offset, value, 4); 319ac34dad2SStefan Richter } 320ac34dad2SStefan Richter 321ac34dad2SStefan Richter static int reg_write(struct isight *isight, int offset, __be32 value) 322ac34dad2SStefan Richter { 323ac34dad2SStefan Richter return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, 324ac34dad2SStefan Richter isight->audio_base + offset, &value, 4); 325ac34dad2SStefan Richter } 326ac34dad2SStefan Richter 3273a691b28SClemens Ladisch static void isight_stop_streaming(struct isight *isight) 3283a691b28SClemens Ladisch { 3293a691b28SClemens Ladisch if (!isight->context) 3303a691b28SClemens Ladisch return; 3313a691b28SClemens Ladisch 3323a691b28SClemens Ladisch fw_iso_context_stop(isight->context); 3333a691b28SClemens Ladisch fw_iso_context_destroy(isight->context); 3343a691b28SClemens Ladisch isight->context = NULL; 3353a691b28SClemens Ladisch fw_iso_resources_free(&isight->resources); 336ac34dad2SStefan Richter reg_write(isight, REG_AUDIO_ENABLE, 0); 3373a691b28SClemens Ladisch } 3383a691b28SClemens Ladisch 3393a691b28SClemens Ladisch static int isight_hw_free(struct snd_pcm_substream *substream) 3403a691b28SClemens Ladisch { 3413a691b28SClemens Ladisch struct isight *isight = substream->private_data; 3423a691b28SClemens Ladisch 34303c29680SClemens Ladisch ACCESS_ONCE(isight->pcm_active) = false; 34403c29680SClemens Ladisch 3453a691b28SClemens Ladisch mutex_lock(&isight->mutex); 3463a691b28SClemens Ladisch isight_stop_streaming(isight); 3473a691b28SClemens Ladisch mutex_unlock(&isight->mutex); 3483a691b28SClemens Ladisch 3493a691b28SClemens Ladisch return snd_pcm_lib_free_vmalloc_buffer(substream); 3503a691b28SClemens Ladisch } 3513a691b28SClemens Ladisch 3523a691b28SClemens Ladisch static int isight_start_streaming(struct isight *isight) 3533a691b28SClemens Ladisch { 3543a691b28SClemens Ladisch unsigned int i; 3553a691b28SClemens Ladisch int err; 3563a691b28SClemens Ladisch 3573a691b28SClemens Ladisch if (isight->context) { 3583a691b28SClemens Ladisch if (isight->packet_index < 0) 3593a691b28SClemens Ladisch isight_stop_streaming(isight); 3603a691b28SClemens Ladisch else 3613a691b28SClemens Ladisch return 0; 3623a691b28SClemens Ladisch } 3633a691b28SClemens Ladisch 364ac34dad2SStefan Richter err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000)); 3653a691b28SClemens Ladisch if (err < 0) 366ac34dad2SStefan Richter goto error; 3673a691b28SClemens Ladisch 3683a691b28SClemens Ladisch err = isight_connect(isight); 3693a691b28SClemens Ladisch if (err < 0) 3703a691b28SClemens Ladisch goto error; 3713a691b28SClemens Ladisch 372ac34dad2SStefan Richter err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE)); 3738839eedaSStefan Richter if (err < 0) 3748839eedaSStefan Richter goto err_resources; 3758839eedaSStefan Richter 3763a691b28SClemens Ladisch isight->context = fw_iso_context_create(isight->device->card, 3773a691b28SClemens Ladisch FW_ISO_CONTEXT_RECEIVE, 3783a691b28SClemens Ladisch isight->resources.channel, 3793a691b28SClemens Ladisch isight->device->max_speed, 3803a691b28SClemens Ladisch 4, isight_packet, isight); 3813a691b28SClemens Ladisch if (IS_ERR(isight->context)) { 3823a691b28SClemens Ladisch err = PTR_ERR(isight->context); 3833a691b28SClemens Ladisch isight->context = NULL; 3843a691b28SClemens Ladisch goto err_resources; 3853a691b28SClemens Ladisch } 3863a691b28SClemens Ladisch 3873a691b28SClemens Ladisch for (i = 0; i < QUEUE_LENGTH; ++i) { 3883a691b28SClemens Ladisch err = fw_iso_context_queue(isight->context, &audio_packet, 3893a691b28SClemens Ladisch &isight->buffer.iso_buffer, 3903a691b28SClemens Ladisch isight->buffer.packets[i].offset); 3913a691b28SClemens Ladisch if (err < 0) 3923a691b28SClemens Ladisch goto err_context; 3933a691b28SClemens Ladisch } 3943a691b28SClemens Ladisch 3953a691b28SClemens Ladisch isight->first_packet = true; 3963a691b28SClemens Ladisch isight->packet_index = 0; 3973a691b28SClemens Ladisch 3983a691b28SClemens Ladisch err = fw_iso_context_start(isight->context, -1, 0, 3993a691b28SClemens Ladisch FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/); 4003a691b28SClemens Ladisch if (err < 0) 4013a691b28SClemens Ladisch goto err_context; 4023a691b28SClemens Ladisch 4033a691b28SClemens Ladisch return 0; 4043a691b28SClemens Ladisch 4053a691b28SClemens Ladisch err_context: 4063a691b28SClemens Ladisch fw_iso_context_destroy(isight->context); 4073a691b28SClemens Ladisch isight->context = NULL; 4083a691b28SClemens Ladisch err_resources: 4093a691b28SClemens Ladisch fw_iso_resources_free(&isight->resources); 410ac34dad2SStefan Richter reg_write(isight, REG_AUDIO_ENABLE, 0); 4113a691b28SClemens Ladisch error: 4123a691b28SClemens Ladisch return err; 4133a691b28SClemens Ladisch } 4143a691b28SClemens Ladisch 4153a691b28SClemens Ladisch static int isight_prepare(struct snd_pcm_substream *substream) 4163a691b28SClemens Ladisch { 4173a691b28SClemens Ladisch struct isight *isight = substream->private_data; 4183a691b28SClemens Ladisch int err; 4193a691b28SClemens Ladisch 4203a691b28SClemens Ladisch isight->buffer_pointer = 0; 4213a691b28SClemens Ladisch isight->period_counter = 0; 4223a691b28SClemens Ladisch 4233a691b28SClemens Ladisch mutex_lock(&isight->mutex); 4243a691b28SClemens Ladisch err = isight_start_streaming(isight); 4253a691b28SClemens Ladisch mutex_unlock(&isight->mutex); 4263a691b28SClemens Ladisch 4273a691b28SClemens Ladisch return err; 4283a691b28SClemens Ladisch } 4293a691b28SClemens Ladisch 4303a691b28SClemens Ladisch static int isight_trigger(struct snd_pcm_substream *substream, int cmd) 4313a691b28SClemens Ladisch { 4323a691b28SClemens Ladisch struct isight *isight = substream->private_data; 4333a691b28SClemens Ladisch 4343a691b28SClemens Ladisch switch (cmd) { 4353a691b28SClemens Ladisch case SNDRV_PCM_TRIGGER_START: 4363a691b28SClemens Ladisch ACCESS_ONCE(isight->pcm_running) = true; 4373a691b28SClemens Ladisch break; 4383a691b28SClemens Ladisch case SNDRV_PCM_TRIGGER_STOP: 4393a691b28SClemens Ladisch ACCESS_ONCE(isight->pcm_running) = false; 4403a691b28SClemens Ladisch break; 4413a691b28SClemens Ladisch default: 4423a691b28SClemens Ladisch return -EINVAL; 4433a691b28SClemens Ladisch } 4443a691b28SClemens Ladisch return 0; 4453a691b28SClemens Ladisch } 4463a691b28SClemens Ladisch 4473a691b28SClemens Ladisch static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream) 4483a691b28SClemens Ladisch { 4493a691b28SClemens Ladisch struct isight *isight = substream->private_data; 4503a691b28SClemens Ladisch 4513a691b28SClemens Ladisch return ACCESS_ONCE(isight->buffer_pointer); 4523a691b28SClemens Ladisch } 4533a691b28SClemens Ladisch 4543a691b28SClemens Ladisch static int isight_create_pcm(struct isight *isight) 4553a691b28SClemens Ladisch { 4563a691b28SClemens Ladisch static struct snd_pcm_ops ops = { 4573a691b28SClemens Ladisch .open = isight_open, 4583a691b28SClemens Ladisch .close = isight_close, 4593a691b28SClemens Ladisch .ioctl = snd_pcm_lib_ioctl, 4603a691b28SClemens Ladisch .hw_params = isight_hw_params, 4613a691b28SClemens Ladisch .hw_free = isight_hw_free, 4623a691b28SClemens Ladisch .prepare = isight_prepare, 4633a691b28SClemens Ladisch .trigger = isight_trigger, 4643a691b28SClemens Ladisch .pointer = isight_pointer, 4653a691b28SClemens Ladisch .page = snd_pcm_lib_get_vmalloc_page, 4663a691b28SClemens Ladisch .mmap = snd_pcm_lib_mmap_vmalloc, 4673a691b28SClemens Ladisch }; 4683a691b28SClemens Ladisch struct snd_pcm *pcm; 4693a691b28SClemens Ladisch int err; 4703a691b28SClemens Ladisch 4713a691b28SClemens Ladisch err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm); 4723a691b28SClemens Ladisch if (err < 0) 4733a691b28SClemens Ladisch return err; 4743a691b28SClemens Ladisch pcm->private_data = isight; 4753a691b28SClemens Ladisch strcpy(pcm->name, "iSight"); 4763a691b28SClemens Ladisch isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 4773a691b28SClemens Ladisch isight->pcm->ops = &ops; 4783a691b28SClemens Ladisch 4793a691b28SClemens Ladisch return 0; 4803a691b28SClemens Ladisch } 4813a691b28SClemens Ladisch 4823a691b28SClemens Ladisch static int isight_gain_info(struct snd_kcontrol *ctl, 4833a691b28SClemens Ladisch struct snd_ctl_elem_info *info) 4843a691b28SClemens Ladisch { 4853a691b28SClemens Ladisch struct isight *isight = ctl->private_data; 4863a691b28SClemens Ladisch 4873a691b28SClemens Ladisch info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4883a691b28SClemens Ladisch info->count = 1; 4893a691b28SClemens Ladisch info->value.integer.min = isight->gain_min; 4903a691b28SClemens Ladisch info->value.integer.max = isight->gain_max; 4913a691b28SClemens Ladisch 4923a691b28SClemens Ladisch return 0; 4933a691b28SClemens Ladisch } 4943a691b28SClemens Ladisch 4953a691b28SClemens Ladisch static int isight_gain_get(struct snd_kcontrol *ctl, 4963a691b28SClemens Ladisch struct snd_ctl_elem_value *value) 4973a691b28SClemens Ladisch { 4983a691b28SClemens Ladisch struct isight *isight = ctl->private_data; 4993a691b28SClemens Ladisch __be32 gain; 5003a691b28SClemens Ladisch int err; 5013a691b28SClemens Ladisch 502ac34dad2SStefan Richter err = reg_read(isight, REG_GAIN, &gain); 5033a691b28SClemens Ladisch if (err < 0) 5043a691b28SClemens Ladisch return err; 5053a691b28SClemens Ladisch 5063a691b28SClemens Ladisch value->value.integer.value[0] = (s32)be32_to_cpu(gain); 5073a691b28SClemens Ladisch 5083a691b28SClemens Ladisch return 0; 5093a691b28SClemens Ladisch } 5103a691b28SClemens Ladisch 5113a691b28SClemens Ladisch static int isight_gain_put(struct snd_kcontrol *ctl, 5123a691b28SClemens Ladisch struct snd_ctl_elem_value *value) 5133a691b28SClemens Ladisch { 5143a691b28SClemens Ladisch struct isight *isight = ctl->private_data; 5153a691b28SClemens Ladisch 5163a691b28SClemens Ladisch if (value->value.integer.value[0] < isight->gain_min || 5173a691b28SClemens Ladisch value->value.integer.value[0] > isight->gain_max) 5183a691b28SClemens Ladisch return -EINVAL; 5193a691b28SClemens Ladisch 520ac34dad2SStefan Richter return reg_write(isight, REG_GAIN, 521ac34dad2SStefan Richter cpu_to_be32(value->value.integer.value[0])); 5223a691b28SClemens Ladisch } 5233a691b28SClemens Ladisch 5243a691b28SClemens Ladisch static int isight_mute_get(struct snd_kcontrol *ctl, 5253a691b28SClemens Ladisch struct snd_ctl_elem_value *value) 5263a691b28SClemens Ladisch { 5273a691b28SClemens Ladisch struct isight *isight = ctl->private_data; 5283a691b28SClemens Ladisch __be32 mute; 5293a691b28SClemens Ladisch int err; 5303a691b28SClemens Ladisch 531ac34dad2SStefan Richter err = reg_read(isight, REG_MUTE, &mute); 5323a691b28SClemens Ladisch if (err < 0) 5333a691b28SClemens Ladisch return err; 5343a691b28SClemens Ladisch 5353a691b28SClemens Ladisch value->value.integer.value[0] = !mute; 5363a691b28SClemens Ladisch 5373a691b28SClemens Ladisch return 0; 5383a691b28SClemens Ladisch } 5393a691b28SClemens Ladisch 5403a691b28SClemens Ladisch static int isight_mute_put(struct snd_kcontrol *ctl, 5413a691b28SClemens Ladisch struct snd_ctl_elem_value *value) 5423a691b28SClemens Ladisch { 5433a691b28SClemens Ladisch struct isight *isight = ctl->private_data; 5443a691b28SClemens Ladisch 545ac34dad2SStefan Richter return reg_write(isight, REG_MUTE, 546ac34dad2SStefan Richter (__force __be32)!value->value.integer.value[0]); 5473a691b28SClemens Ladisch } 5483a691b28SClemens Ladisch 5493a691b28SClemens Ladisch static int isight_create_mixer(struct isight *isight) 5503a691b28SClemens Ladisch { 5513a691b28SClemens Ladisch static const struct snd_kcontrol_new gain_control = { 5523a691b28SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5533a691b28SClemens Ladisch .name = "Mic Capture Volume", 5543a691b28SClemens Ladisch .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 5553a691b28SClemens Ladisch SNDRV_CTL_ELEM_ACCESS_TLV_READ, 5563a691b28SClemens Ladisch .info = isight_gain_info, 5573a691b28SClemens Ladisch .get = isight_gain_get, 5583a691b28SClemens Ladisch .put = isight_gain_put, 5593a691b28SClemens Ladisch }; 5603a691b28SClemens Ladisch static const struct snd_kcontrol_new mute_control = { 5613a691b28SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5623a691b28SClemens Ladisch .name = "Mic Capture Switch", 5633a691b28SClemens Ladisch .info = snd_ctl_boolean_mono_info, 5643a691b28SClemens Ladisch .get = isight_mute_get, 5653a691b28SClemens Ladisch .put = isight_mute_put, 5663a691b28SClemens Ladisch }; 5673a691b28SClemens Ladisch __be32 value; 5683a691b28SClemens Ladisch struct snd_kcontrol *ctl; 5693a691b28SClemens Ladisch int err; 5703a691b28SClemens Ladisch 571ac34dad2SStefan Richter err = reg_read(isight, REG_GAIN_RAW_START, &value); 5723a691b28SClemens Ladisch if (err < 0) 5733a691b28SClemens Ladisch return err; 5743a691b28SClemens Ladisch isight->gain_min = be32_to_cpu(value); 5753a691b28SClemens Ladisch 576ac34dad2SStefan Richter err = reg_read(isight, REG_GAIN_RAW_END, &value); 5773a691b28SClemens Ladisch if (err < 0) 5783a691b28SClemens Ladisch return err; 5793a691b28SClemens Ladisch isight->gain_max = be32_to_cpu(value); 5803a691b28SClemens Ladisch 5813a691b28SClemens Ladisch isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX; 5823a691b28SClemens Ladisch isight->gain_tlv[1] = 2 * sizeof(unsigned int); 583ac34dad2SStefan Richter 584ac34dad2SStefan Richter err = reg_read(isight, REG_GAIN_DB_START, &value); 5853a691b28SClemens Ladisch if (err < 0) 5863a691b28SClemens Ladisch return err; 5873a691b28SClemens Ladisch isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100; 588ac34dad2SStefan Richter 589ac34dad2SStefan Richter err = reg_read(isight, REG_GAIN_DB_END, &value); 5903a691b28SClemens Ladisch if (err < 0) 5913a691b28SClemens Ladisch return err; 5923a691b28SClemens Ladisch isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100; 5933a691b28SClemens Ladisch 5943a691b28SClemens Ladisch ctl = snd_ctl_new1(&gain_control, isight); 5953a691b28SClemens Ladisch if (ctl) 5963a691b28SClemens Ladisch ctl->tlv.p = isight->gain_tlv; 5973a691b28SClemens Ladisch err = snd_ctl_add(isight->card, ctl); 5983a691b28SClemens Ladisch if (err < 0) 5993a691b28SClemens Ladisch return err; 6003a691b28SClemens Ladisch 6013a691b28SClemens Ladisch err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight)); 6023a691b28SClemens Ladisch if (err < 0) 6033a691b28SClemens Ladisch return err; 6043a691b28SClemens Ladisch 6053a691b28SClemens Ladisch return 0; 6063a691b28SClemens Ladisch } 6073a691b28SClemens Ladisch 6083a691b28SClemens Ladisch static void isight_card_free(struct snd_card *card) 6093a691b28SClemens Ladisch { 6103a691b28SClemens Ladisch struct isight *isight = card->private_data; 6113a691b28SClemens Ladisch 6123a691b28SClemens Ladisch fw_iso_resources_destroy(&isight->resources); 6133a691b28SClemens Ladisch fw_unit_put(isight->unit); 6143a691b28SClemens Ladisch mutex_destroy(&isight->mutex); 6153a691b28SClemens Ladisch } 6163a691b28SClemens Ladisch 6173a691b28SClemens Ladisch static u64 get_unit_base(struct fw_unit *unit) 6183a691b28SClemens Ladisch { 6193a691b28SClemens Ladisch struct fw_csr_iterator i; 6203a691b28SClemens Ladisch int key, value; 6213a691b28SClemens Ladisch 6223a691b28SClemens Ladisch fw_csr_iterator_init(&i, unit->directory); 6233a691b28SClemens Ladisch while (fw_csr_iterator_next(&i, &key, &value)) 6243a691b28SClemens Ladisch if (key == CSR_OFFSET) 6253a691b28SClemens Ladisch return CSR_REGISTER_BASE + value * 4; 6263a691b28SClemens Ladisch return 0; 6273a691b28SClemens Ladisch } 6283a691b28SClemens Ladisch 629*94a87157SStefan Richter static int isight_probe(struct fw_unit *unit, 630*94a87157SStefan Richter const struct ieee1394_device_id *id) 6313a691b28SClemens Ladisch { 6323a691b28SClemens Ladisch struct fw_device *fw_dev = fw_parent_device(unit); 6333a691b28SClemens Ladisch struct snd_card *card; 6343a691b28SClemens Ladisch struct isight *isight; 6353a691b28SClemens Ladisch int err; 6363a691b28SClemens Ladisch 6373a691b28SClemens Ladisch err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*isight), &card); 6383a691b28SClemens Ladisch if (err < 0) 6393a691b28SClemens Ladisch return err; 640*94a87157SStefan Richter snd_card_set_dev(card, &unit->device); 6413a691b28SClemens Ladisch 6423a691b28SClemens Ladisch isight = card->private_data; 6433a691b28SClemens Ladisch isight->card = card; 6443a691b28SClemens Ladisch mutex_init(&isight->mutex); 6453a691b28SClemens Ladisch isight->unit = fw_unit_get(unit); 64621076226SStefan Richter isight->device = fw_dev; 6473a691b28SClemens Ladisch isight->audio_base = get_unit_base(unit); 6483a691b28SClemens Ladisch if (!isight->audio_base) { 6493a691b28SClemens Ladisch dev_err(&unit->device, "audio unit base not found\n"); 6503a691b28SClemens Ladisch err = -ENXIO; 6513a691b28SClemens Ladisch goto err_unit; 6523a691b28SClemens Ladisch } 6533a691b28SClemens Ladisch fw_iso_resources_init(&isight->resources, unit); 6543a691b28SClemens Ladisch 6553a691b28SClemens Ladisch card->private_free = isight_card_free; 6563a691b28SClemens Ladisch 6573a691b28SClemens Ladisch strcpy(card->driver, "iSight"); 6583a691b28SClemens Ladisch strcpy(card->shortname, "Apple iSight"); 6593a691b28SClemens Ladisch snprintf(card->longname, sizeof(card->longname), 6603a691b28SClemens Ladisch "Apple iSight (GUID %08x%08x) at %s, S%d", 6613a691b28SClemens Ladisch fw_dev->config_rom[3], fw_dev->config_rom[4], 6623a691b28SClemens Ladisch dev_name(&unit->device), 100 << fw_dev->max_speed); 6633a691b28SClemens Ladisch strcpy(card->mixername, "iSight"); 6643a691b28SClemens Ladisch 6653a691b28SClemens Ladisch err = isight_create_pcm(isight); 6663a691b28SClemens Ladisch if (err < 0) 6673a691b28SClemens Ladisch goto error; 6683a691b28SClemens Ladisch 6693a691b28SClemens Ladisch err = isight_create_mixer(isight); 6703a691b28SClemens Ladisch if (err < 0) 6713a691b28SClemens Ladisch goto error; 6723a691b28SClemens Ladisch 6733a691b28SClemens Ladisch err = snd_card_register(card); 6743a691b28SClemens Ladisch if (err < 0) 6753a691b28SClemens Ladisch goto error; 6763a691b28SClemens Ladisch 677*94a87157SStefan Richter dev_set_drvdata(&unit->device, isight); 6783a691b28SClemens Ladisch 6793a691b28SClemens Ladisch return 0; 6803a691b28SClemens Ladisch 6813a691b28SClemens Ladisch err_unit: 6823a691b28SClemens Ladisch fw_unit_put(isight->unit); 6833a691b28SClemens Ladisch mutex_destroy(&isight->mutex); 6843a691b28SClemens Ladisch error: 6853a691b28SClemens Ladisch snd_card_free(card); 6863a691b28SClemens Ladisch return err; 6873a691b28SClemens Ladisch } 6883a691b28SClemens Ladisch 6893a691b28SClemens Ladisch static void isight_bus_reset(struct fw_unit *unit) 6903a691b28SClemens Ladisch { 6913a691b28SClemens Ladisch struct isight *isight = dev_get_drvdata(&unit->device); 6923a691b28SClemens Ladisch 6933a691b28SClemens Ladisch if (fw_iso_resources_update(&isight->resources) < 0) { 6943a691b28SClemens Ladisch isight_pcm_abort(isight); 695f3f7c183SClemens Ladisch 696f3f7c183SClemens Ladisch mutex_lock(&isight->mutex); 6973a691b28SClemens Ladisch isight_stop_streaming(isight); 6983a691b28SClemens Ladisch mutex_unlock(&isight->mutex); 6993a691b28SClemens Ladisch } 700f3f7c183SClemens Ladisch } 7013a691b28SClemens Ladisch 702*94a87157SStefan Richter static void isight_remove(struct fw_unit *unit) 703*94a87157SStefan Richter { 704*94a87157SStefan Richter struct isight *isight = dev_get_drvdata(&unit->device); 705*94a87157SStefan Richter 706*94a87157SStefan Richter isight_pcm_abort(isight); 707*94a87157SStefan Richter 708*94a87157SStefan Richter snd_card_disconnect(isight->card); 709*94a87157SStefan Richter 710*94a87157SStefan Richter mutex_lock(&isight->mutex); 711*94a87157SStefan Richter isight_stop_streaming(isight); 712*94a87157SStefan Richter mutex_unlock(&isight->mutex); 713*94a87157SStefan Richter 714*94a87157SStefan Richter snd_card_free_when_closed(isight->card); 715*94a87157SStefan Richter } 716*94a87157SStefan Richter 7173a691b28SClemens Ladisch static const struct ieee1394_device_id isight_id_table[] = { 7183a691b28SClemens Ladisch { 7193a691b28SClemens Ladisch .match_flags = IEEE1394_MATCH_SPECIFIER_ID | 7203a691b28SClemens Ladisch IEEE1394_MATCH_VERSION, 7213a691b28SClemens Ladisch .specifier_id = OUI_APPLE, 7223a691b28SClemens Ladisch .version = SW_ISIGHT_AUDIO, 7233a691b28SClemens Ladisch }, 7243a691b28SClemens Ladisch { } 7253a691b28SClemens Ladisch }; 7263a691b28SClemens Ladisch MODULE_DEVICE_TABLE(ieee1394, isight_id_table); 7273a691b28SClemens Ladisch 7283a691b28SClemens Ladisch static struct fw_driver isight_driver = { 7293a691b28SClemens Ladisch .driver = { 7303a691b28SClemens Ladisch .owner = THIS_MODULE, 7313a691b28SClemens Ladisch .name = KBUILD_MODNAME, 7323a691b28SClemens Ladisch .bus = &fw_bus_type, 7333a691b28SClemens Ladisch }, 734*94a87157SStefan Richter .probe = isight_probe, 7353a691b28SClemens Ladisch .update = isight_bus_reset, 736*94a87157SStefan Richter .remove = isight_remove, 7373a691b28SClemens Ladisch .id_table = isight_id_table, 7383a691b28SClemens Ladisch }; 7393a691b28SClemens Ladisch 7403a691b28SClemens Ladisch static int __init alsa_isight_init(void) 7413a691b28SClemens Ladisch { 7423a691b28SClemens Ladisch return driver_register(&isight_driver.driver); 7433a691b28SClemens Ladisch } 7443a691b28SClemens Ladisch 7453a691b28SClemens Ladisch static void __exit alsa_isight_exit(void) 7463a691b28SClemens Ladisch { 7473a691b28SClemens Ladisch driver_unregister(&isight_driver.driver); 7483a691b28SClemens Ladisch } 7493a691b28SClemens Ladisch 7503a691b28SClemens Ladisch module_init(alsa_isight_init); 7513a691b28SClemens Ladisch module_exit(alsa_isight_exit); 752