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 fw_address_handler iris_handler; 553a691b28SClemens Ladisch struct snd_pcm_substream *pcm; 563a691b28SClemens Ladisch struct mutex mutex; 573a691b28SClemens Ladisch struct iso_packets_buffer buffer; 583a691b28SClemens Ladisch struct fw_iso_resources resources; 593a691b28SClemens Ladisch struct fw_iso_context *context; 6003c29680SClemens Ladisch bool pcm_active; 613a691b28SClemens Ladisch bool pcm_running; 623a691b28SClemens Ladisch bool first_packet; 633a691b28SClemens Ladisch int packet_index; 643a691b28SClemens Ladisch u32 total_samples; 653a691b28SClemens Ladisch unsigned int buffer_pointer; 663a691b28SClemens Ladisch unsigned int period_counter; 673a691b28SClemens Ladisch s32 gain_min, gain_max; 683a691b28SClemens Ladisch unsigned int gain_tlv[4]; 693a691b28SClemens Ladisch }; 703a691b28SClemens Ladisch 713a691b28SClemens Ladisch struct audio_payload { 723a691b28SClemens Ladisch __be32 sample_count; 733a691b28SClemens Ladisch __be32 signature; 743a691b28SClemens Ladisch __be32 sample_total; 753a691b28SClemens Ladisch __be32 reserved; 763a691b28SClemens Ladisch __be16 samples[2 * MAX_FRAMES_PER_PACKET]; 773a691b28SClemens Ladisch }; 783a691b28SClemens Ladisch 793a691b28SClemens Ladisch MODULE_DESCRIPTION("iSight audio driver"); 803a691b28SClemens Ladisch MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); 813a691b28SClemens Ladisch MODULE_LICENSE("GPL v2"); 823a691b28SClemens Ladisch 833a691b28SClemens Ladisch static struct fw_iso_packet audio_packet = { 843a691b28SClemens Ladisch .payload_length = sizeof(struct audio_payload), 853a691b28SClemens Ladisch .interrupt = 1, 86f2934cd4SClemens Ladisch .header_length = 4, 873a691b28SClemens Ladisch }; 883a691b28SClemens Ladisch 893a691b28SClemens Ladisch static void isight_update_pointers(struct isight *isight, unsigned int count) 903a691b28SClemens Ladisch { 913a691b28SClemens Ladisch struct snd_pcm_runtime *runtime = isight->pcm->runtime; 923a691b28SClemens Ladisch unsigned int ptr; 933a691b28SClemens Ladisch 943a691b28SClemens Ladisch smp_wmb(); /* update buffer data before buffer pointer */ 953a691b28SClemens Ladisch 963a691b28SClemens Ladisch ptr = isight->buffer_pointer; 973a691b28SClemens Ladisch ptr += count; 983a691b28SClemens Ladisch if (ptr >= runtime->buffer_size) 993a691b28SClemens Ladisch ptr -= runtime->buffer_size; 1003a691b28SClemens Ladisch ACCESS_ONCE(isight->buffer_pointer) = ptr; 1013a691b28SClemens Ladisch 1023a691b28SClemens Ladisch isight->period_counter += count; 1033a691b28SClemens Ladisch if (isight->period_counter >= runtime->period_size) { 1043a691b28SClemens Ladisch isight->period_counter -= runtime->period_size; 1053a691b28SClemens Ladisch snd_pcm_period_elapsed(isight->pcm); 1063a691b28SClemens Ladisch } 1073a691b28SClemens Ladisch } 1083a691b28SClemens Ladisch 1093a691b28SClemens Ladisch static void isight_samples(struct isight *isight, 1103a691b28SClemens Ladisch const __be16 *samples, unsigned int count) 1113a691b28SClemens Ladisch { 1123a691b28SClemens Ladisch struct snd_pcm_runtime *runtime; 1133a691b28SClemens Ladisch unsigned int count1; 1143a691b28SClemens Ladisch 1153a691b28SClemens Ladisch if (!ACCESS_ONCE(isight->pcm_running)) 1163a691b28SClemens Ladisch return; 1173a691b28SClemens Ladisch 1183a691b28SClemens Ladisch runtime = isight->pcm->runtime; 1193a691b28SClemens Ladisch if (isight->buffer_pointer + count <= runtime->buffer_size) { 1203a691b28SClemens Ladisch memcpy(runtime->dma_area + isight->buffer_pointer * 4, 1213a691b28SClemens Ladisch samples, count * 4); 1223a691b28SClemens Ladisch } else { 1233a691b28SClemens Ladisch count1 = runtime->buffer_size - isight->buffer_pointer; 1243a691b28SClemens Ladisch memcpy(runtime->dma_area + isight->buffer_pointer * 4, 1253a691b28SClemens Ladisch samples, count1 * 4); 1263a691b28SClemens Ladisch samples += count1 * 2; 1273a691b28SClemens Ladisch memcpy(runtime->dma_area, samples, (count - count1) * 4); 1283a691b28SClemens Ladisch } 1293a691b28SClemens Ladisch 1303a691b28SClemens Ladisch isight_update_pointers(isight, count); 1313a691b28SClemens Ladisch } 1323a691b28SClemens Ladisch 1333a691b28SClemens Ladisch static void isight_pcm_abort(struct isight *isight) 1343a691b28SClemens Ladisch { 1353a691b28SClemens Ladisch unsigned long flags; 1363a691b28SClemens Ladisch 13703c29680SClemens Ladisch if (ACCESS_ONCE(isight->pcm_active)) { 1383a691b28SClemens Ladisch snd_pcm_stream_lock_irqsave(isight->pcm, flags); 1393a691b28SClemens Ladisch if (snd_pcm_running(isight->pcm)) 1403a691b28SClemens Ladisch snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN); 1413a691b28SClemens Ladisch snd_pcm_stream_unlock_irqrestore(isight->pcm, flags); 1423a691b28SClemens Ladisch } 14303c29680SClemens Ladisch } 1443a691b28SClemens Ladisch 1453a691b28SClemens Ladisch static void isight_dropped_samples(struct isight *isight, unsigned int total) 1463a691b28SClemens Ladisch { 1473a691b28SClemens Ladisch struct snd_pcm_runtime *runtime; 1483a691b28SClemens Ladisch u32 dropped; 1493a691b28SClemens Ladisch unsigned int count1; 1503a691b28SClemens Ladisch 1513a691b28SClemens Ladisch if (!ACCESS_ONCE(isight->pcm_running)) 1523a691b28SClemens Ladisch return; 1533a691b28SClemens Ladisch 1543a691b28SClemens Ladisch runtime = isight->pcm->runtime; 1553a691b28SClemens Ladisch dropped = total - isight->total_samples; 1563a691b28SClemens Ladisch if (dropped < runtime->buffer_size) { 1573a691b28SClemens Ladisch if (isight->buffer_pointer + dropped <= runtime->buffer_size) { 1583a691b28SClemens Ladisch memset(runtime->dma_area + isight->buffer_pointer * 4, 1593a691b28SClemens Ladisch 0, dropped * 4); 1603a691b28SClemens Ladisch } else { 1613a691b28SClemens Ladisch count1 = runtime->buffer_size - isight->buffer_pointer; 1623a691b28SClemens Ladisch memset(runtime->dma_area + isight->buffer_pointer * 4, 1633a691b28SClemens Ladisch 0, count1 * 4); 1643a691b28SClemens Ladisch memset(runtime->dma_area, 0, (dropped - count1) * 4); 1653a691b28SClemens Ladisch } 1663a691b28SClemens Ladisch isight_update_pointers(isight, dropped); 1673a691b28SClemens Ladisch } else { 1683a691b28SClemens Ladisch isight_pcm_abort(isight); 1693a691b28SClemens Ladisch } 1703a691b28SClemens Ladisch } 1713a691b28SClemens Ladisch 1723a691b28SClemens Ladisch static void isight_packet(struct fw_iso_context *context, u32 cycle, 1733a691b28SClemens Ladisch size_t header_length, void *header, void *data) 1743a691b28SClemens Ladisch { 1753a691b28SClemens Ladisch struct isight *isight = data; 1763a691b28SClemens Ladisch const struct audio_payload *payload; 1773a691b28SClemens Ladisch unsigned int index, length, count, total; 1783a691b28SClemens Ladisch int err; 1793a691b28SClemens Ladisch 1803a691b28SClemens Ladisch if (isight->packet_index < 0) 1813a691b28SClemens Ladisch return; 1823a691b28SClemens Ladisch index = isight->packet_index; 1833a691b28SClemens Ladisch payload = isight->buffer.packets[index].buffer; 1843a691b28SClemens Ladisch length = be32_to_cpup(header) >> 16; 1853a691b28SClemens Ladisch 1863a691b28SClemens Ladisch if (likely(length >= 16 && 1873a691b28SClemens Ladisch payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) { 1883a691b28SClemens Ladisch count = be32_to_cpu(payload->sample_count); 1893a691b28SClemens Ladisch if (likely(count <= (length - 16) / 4)) { 1903a691b28SClemens Ladisch total = be32_to_cpu(payload->sample_total); 1913a691b28SClemens Ladisch if (unlikely(total != isight->total_samples)) { 1923a691b28SClemens Ladisch if (!isight->first_packet) 1933a691b28SClemens Ladisch isight_dropped_samples(isight, total); 1943a691b28SClemens Ladisch isight->first_packet = false; 1953a691b28SClemens Ladisch isight->total_samples = total; 1963a691b28SClemens Ladisch } 1973a691b28SClemens Ladisch 1983a691b28SClemens Ladisch isight_samples(isight, payload->samples, count); 1993a691b28SClemens Ladisch isight->total_samples += count; 2003a691b28SClemens Ladisch } 2013a691b28SClemens Ladisch } 2023a691b28SClemens Ladisch 2033a691b28SClemens Ladisch err = fw_iso_context_queue(isight->context, &audio_packet, 2043a691b28SClemens Ladisch &isight->buffer.iso_buffer, 2053a691b28SClemens Ladisch isight->buffer.packets[index].offset); 2063a691b28SClemens Ladisch if (err < 0) { 2073a691b28SClemens Ladisch dev_err(&isight->unit->device, "queueing error: %d\n", err); 2083a691b28SClemens Ladisch isight_pcm_abort(isight); 2093a691b28SClemens Ladisch isight->packet_index = -1; 2103a691b28SClemens Ladisch return; 2113a691b28SClemens Ladisch } 212*cf6f1ff1SClemens Ladisch fw_iso_context_queue_flush(isight->context); 2133a691b28SClemens Ladisch 214898732d1SClemens Ladisch if (++index >= QUEUE_LENGTH) 215898732d1SClemens Ladisch index = 0; 2163a691b28SClemens Ladisch isight->packet_index = index; 2173a691b28SClemens Ladisch } 2183a691b28SClemens Ladisch 2193a691b28SClemens Ladisch static int isight_connect(struct isight *isight) 2203a691b28SClemens Ladisch { 2213a691b28SClemens Ladisch int ch, err, rcode, errors = 0; 2223a691b28SClemens Ladisch __be32 value; 2233a691b28SClemens Ladisch 2243a691b28SClemens Ladisch retry_after_bus_reset: 2253a691b28SClemens Ladisch ch = fw_iso_resources_allocate(&isight->resources, 2263a691b28SClemens Ladisch sizeof(struct audio_payload), 2273a691b28SClemens Ladisch isight->device->max_speed); 2283a691b28SClemens Ladisch if (ch < 0) { 2293a691b28SClemens Ladisch err = ch; 2303a691b28SClemens Ladisch goto error; 2313a691b28SClemens Ladisch } 2323a691b28SClemens Ladisch 2333a691b28SClemens Ladisch value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT)); 2343a691b28SClemens Ladisch for (;;) { 2353a691b28SClemens Ladisch rcode = fw_run_transaction( 2363a691b28SClemens Ladisch isight->device->card, 2373a691b28SClemens Ladisch TCODE_WRITE_QUADLET_REQUEST, 2383a691b28SClemens Ladisch isight->device->node_id, 2393a691b28SClemens Ladisch isight->resources.generation, 2403a691b28SClemens Ladisch isight->device->max_speed, 2413a691b28SClemens Ladisch isight->audio_base + REG_ISO_TX_CONFIG, 2423a691b28SClemens Ladisch &value, 4); 2433a691b28SClemens Ladisch if (rcode == RCODE_COMPLETE) { 2443a691b28SClemens Ladisch return 0; 2453a691b28SClemens Ladisch } else if (rcode == RCODE_GENERATION) { 2463a691b28SClemens Ladisch fw_iso_resources_free(&isight->resources); 2473a691b28SClemens Ladisch goto retry_after_bus_reset; 2483a691b28SClemens Ladisch } else if (rcode_is_permanent_error(rcode) || ++errors >= 3) { 2493a691b28SClemens Ladisch err = -EIO; 2503a691b28SClemens Ladisch goto err_resources; 2513a691b28SClemens Ladisch } 2523a691b28SClemens Ladisch msleep(5); 2533a691b28SClemens Ladisch } 2543a691b28SClemens Ladisch 2553a691b28SClemens Ladisch err_resources: 2563a691b28SClemens Ladisch fw_iso_resources_free(&isight->resources); 2573a691b28SClemens Ladisch error: 2583a691b28SClemens Ladisch return err; 2593a691b28SClemens Ladisch } 2603a691b28SClemens Ladisch 2613a691b28SClemens Ladisch static int isight_open(struct snd_pcm_substream *substream) 2623a691b28SClemens Ladisch { 2633a691b28SClemens Ladisch static const struct snd_pcm_hardware hardware = { 2643a691b28SClemens Ladisch .info = SNDRV_PCM_INFO_MMAP | 2653a691b28SClemens Ladisch SNDRV_PCM_INFO_MMAP_VALID | 2663a691b28SClemens Ladisch SNDRV_PCM_INFO_BATCH | 2673a691b28SClemens Ladisch SNDRV_PCM_INFO_INTERLEAVED | 2683a691b28SClemens Ladisch SNDRV_PCM_INFO_BLOCK_TRANSFER, 2693a691b28SClemens Ladisch .formats = SNDRV_PCM_FMTBIT_S16_BE, 2703a691b28SClemens Ladisch .rates = SNDRV_PCM_RATE_48000, 2713a691b28SClemens Ladisch .rate_min = 48000, 2723a691b28SClemens Ladisch .rate_max = 48000, 2733a691b28SClemens Ladisch .channels_min = 2, 2743a691b28SClemens Ladisch .channels_max = 2, 2753a691b28SClemens Ladisch .buffer_bytes_max = 4 * 1024 * 1024, 2763a691b28SClemens Ladisch .period_bytes_min = MAX_FRAMES_PER_PACKET * 4, 2773a691b28SClemens Ladisch .period_bytes_max = 1024 * 1024, 2783a691b28SClemens Ladisch .periods_min = 2, 2793a691b28SClemens Ladisch .periods_max = UINT_MAX, 2803a691b28SClemens Ladisch }; 2813a691b28SClemens Ladisch struct isight *isight = substream->private_data; 2823a691b28SClemens Ladisch 2833a691b28SClemens Ladisch substream->runtime->hw = hardware; 2843a691b28SClemens Ladisch 2853a691b28SClemens Ladisch return iso_packets_buffer_init(&isight->buffer, isight->unit, 2863a691b28SClemens Ladisch QUEUE_LENGTH, 2873a691b28SClemens Ladisch sizeof(struct audio_payload), 2883a691b28SClemens Ladisch DMA_FROM_DEVICE); 2893a691b28SClemens Ladisch } 2903a691b28SClemens Ladisch 2913a691b28SClemens Ladisch static int isight_close(struct snd_pcm_substream *substream) 2923a691b28SClemens Ladisch { 2933a691b28SClemens Ladisch struct isight *isight = substream->private_data; 2943a691b28SClemens Ladisch 2953a691b28SClemens Ladisch iso_packets_buffer_destroy(&isight->buffer, isight->unit); 2963a691b28SClemens Ladisch 2973a691b28SClemens Ladisch return 0; 2983a691b28SClemens Ladisch } 2993a691b28SClemens Ladisch 3003a691b28SClemens Ladisch static int isight_hw_params(struct snd_pcm_substream *substream, 3013a691b28SClemens Ladisch struct snd_pcm_hw_params *hw_params) 3023a691b28SClemens Ladisch { 30303c29680SClemens Ladisch struct isight *isight = substream->private_data; 30403c29680SClemens Ladisch int err; 30503c29680SClemens Ladisch 30603c29680SClemens Ladisch err = snd_pcm_lib_alloc_vmalloc_buffer(substream, 3073a691b28SClemens Ladisch params_buffer_bytes(hw_params)); 30803c29680SClemens Ladisch if (err < 0) 30903c29680SClemens Ladisch return err; 31003c29680SClemens Ladisch 31103c29680SClemens Ladisch ACCESS_ONCE(isight->pcm_active) = true; 31203c29680SClemens Ladisch 31303c29680SClemens Ladisch return 0; 3143a691b28SClemens Ladisch } 3153a691b28SClemens Ladisch 316ac34dad2SStefan Richter static int reg_read(struct isight *isight, int offset, __be32 *value) 317ac34dad2SStefan Richter { 318ac34dad2SStefan Richter return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, 319ac34dad2SStefan Richter isight->audio_base + offset, value, 4); 320ac34dad2SStefan Richter } 321ac34dad2SStefan Richter 322ac34dad2SStefan Richter static int reg_write(struct isight *isight, int offset, __be32 value) 323ac34dad2SStefan Richter { 324ac34dad2SStefan Richter return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, 325ac34dad2SStefan Richter isight->audio_base + offset, &value, 4); 326ac34dad2SStefan Richter } 327ac34dad2SStefan Richter 3283a691b28SClemens Ladisch static void isight_stop_streaming(struct isight *isight) 3293a691b28SClemens Ladisch { 3303a691b28SClemens Ladisch if (!isight->context) 3313a691b28SClemens Ladisch return; 3323a691b28SClemens Ladisch 3333a691b28SClemens Ladisch fw_iso_context_stop(isight->context); 3343a691b28SClemens Ladisch fw_iso_context_destroy(isight->context); 3353a691b28SClemens Ladisch isight->context = NULL; 3363a691b28SClemens Ladisch fw_iso_resources_free(&isight->resources); 337ac34dad2SStefan Richter reg_write(isight, REG_AUDIO_ENABLE, 0); 3383a691b28SClemens Ladisch } 3393a691b28SClemens Ladisch 3403a691b28SClemens Ladisch static int isight_hw_free(struct snd_pcm_substream *substream) 3413a691b28SClemens Ladisch { 3423a691b28SClemens Ladisch struct isight *isight = substream->private_data; 3433a691b28SClemens Ladisch 34403c29680SClemens Ladisch ACCESS_ONCE(isight->pcm_active) = false; 34503c29680SClemens Ladisch 3463a691b28SClemens Ladisch mutex_lock(&isight->mutex); 3473a691b28SClemens Ladisch isight_stop_streaming(isight); 3483a691b28SClemens Ladisch mutex_unlock(&isight->mutex); 3493a691b28SClemens Ladisch 3503a691b28SClemens Ladisch return snd_pcm_lib_free_vmalloc_buffer(substream); 3513a691b28SClemens Ladisch } 3523a691b28SClemens Ladisch 3533a691b28SClemens Ladisch static int isight_start_streaming(struct isight *isight) 3543a691b28SClemens Ladisch { 3553a691b28SClemens Ladisch unsigned int i; 3563a691b28SClemens Ladisch int err; 3573a691b28SClemens Ladisch 3583a691b28SClemens Ladisch if (isight->context) { 3593a691b28SClemens Ladisch if (isight->packet_index < 0) 3603a691b28SClemens Ladisch isight_stop_streaming(isight); 3613a691b28SClemens Ladisch else 3623a691b28SClemens Ladisch return 0; 3633a691b28SClemens Ladisch } 3643a691b28SClemens Ladisch 365ac34dad2SStefan Richter err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000)); 3663a691b28SClemens Ladisch if (err < 0) 367ac34dad2SStefan Richter goto error; 3683a691b28SClemens Ladisch 3693a691b28SClemens Ladisch err = isight_connect(isight); 3703a691b28SClemens Ladisch if (err < 0) 3713a691b28SClemens Ladisch goto error; 3723a691b28SClemens Ladisch 373ac34dad2SStefan Richter err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE)); 3748839eedaSStefan Richter if (err < 0) 3758839eedaSStefan Richter goto err_resources; 3768839eedaSStefan Richter 3773a691b28SClemens Ladisch isight->context = fw_iso_context_create(isight->device->card, 3783a691b28SClemens Ladisch FW_ISO_CONTEXT_RECEIVE, 3793a691b28SClemens Ladisch isight->resources.channel, 3803a691b28SClemens Ladisch isight->device->max_speed, 3813a691b28SClemens Ladisch 4, isight_packet, isight); 3823a691b28SClemens Ladisch if (IS_ERR(isight->context)) { 3833a691b28SClemens Ladisch err = PTR_ERR(isight->context); 3843a691b28SClemens Ladisch isight->context = NULL; 3853a691b28SClemens Ladisch goto err_resources; 3863a691b28SClemens Ladisch } 3873a691b28SClemens Ladisch 3883a691b28SClemens Ladisch for (i = 0; i < QUEUE_LENGTH; ++i) { 3893a691b28SClemens Ladisch err = fw_iso_context_queue(isight->context, &audio_packet, 3903a691b28SClemens Ladisch &isight->buffer.iso_buffer, 3913a691b28SClemens Ladisch isight->buffer.packets[i].offset); 3923a691b28SClemens Ladisch if (err < 0) 3933a691b28SClemens Ladisch goto err_context; 3943a691b28SClemens Ladisch } 3953a691b28SClemens Ladisch 3963a691b28SClemens Ladisch isight->first_packet = true; 3973a691b28SClemens Ladisch isight->packet_index = 0; 3983a691b28SClemens Ladisch 3993a691b28SClemens Ladisch err = fw_iso_context_start(isight->context, -1, 0, 4003a691b28SClemens Ladisch FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/); 4013a691b28SClemens Ladisch if (err < 0) 4023a691b28SClemens Ladisch goto err_context; 4033a691b28SClemens Ladisch 4043a691b28SClemens Ladisch return 0; 4053a691b28SClemens Ladisch 4063a691b28SClemens Ladisch err_context: 4073a691b28SClemens Ladisch fw_iso_context_destroy(isight->context); 4083a691b28SClemens Ladisch isight->context = NULL; 4093a691b28SClemens Ladisch err_resources: 4103a691b28SClemens Ladisch fw_iso_resources_free(&isight->resources); 411ac34dad2SStefan Richter reg_write(isight, REG_AUDIO_ENABLE, 0); 4123a691b28SClemens Ladisch error: 4133a691b28SClemens Ladisch return err; 4143a691b28SClemens Ladisch } 4153a691b28SClemens Ladisch 4163a691b28SClemens Ladisch static int isight_prepare(struct snd_pcm_substream *substream) 4173a691b28SClemens Ladisch { 4183a691b28SClemens Ladisch struct isight *isight = substream->private_data; 4193a691b28SClemens Ladisch int err; 4203a691b28SClemens Ladisch 4213a691b28SClemens Ladisch isight->buffer_pointer = 0; 4223a691b28SClemens Ladisch isight->period_counter = 0; 4233a691b28SClemens Ladisch 4243a691b28SClemens Ladisch mutex_lock(&isight->mutex); 4253a691b28SClemens Ladisch err = isight_start_streaming(isight); 4263a691b28SClemens Ladisch mutex_unlock(&isight->mutex); 4273a691b28SClemens Ladisch 4283a691b28SClemens Ladisch return err; 4293a691b28SClemens Ladisch } 4303a691b28SClemens Ladisch 4313a691b28SClemens Ladisch static int isight_trigger(struct snd_pcm_substream *substream, int cmd) 4323a691b28SClemens Ladisch { 4333a691b28SClemens Ladisch struct isight *isight = substream->private_data; 4343a691b28SClemens Ladisch 4353a691b28SClemens Ladisch switch (cmd) { 4363a691b28SClemens Ladisch case SNDRV_PCM_TRIGGER_START: 4373a691b28SClemens Ladisch ACCESS_ONCE(isight->pcm_running) = true; 4383a691b28SClemens Ladisch break; 4393a691b28SClemens Ladisch case SNDRV_PCM_TRIGGER_STOP: 4403a691b28SClemens Ladisch ACCESS_ONCE(isight->pcm_running) = false; 4413a691b28SClemens Ladisch break; 4423a691b28SClemens Ladisch default: 4433a691b28SClemens Ladisch return -EINVAL; 4443a691b28SClemens Ladisch } 4453a691b28SClemens Ladisch return 0; 4463a691b28SClemens Ladisch } 4473a691b28SClemens Ladisch 4483a691b28SClemens Ladisch static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream) 4493a691b28SClemens Ladisch { 4503a691b28SClemens Ladisch struct isight *isight = substream->private_data; 4513a691b28SClemens Ladisch 4523a691b28SClemens Ladisch return ACCESS_ONCE(isight->buffer_pointer); 4533a691b28SClemens Ladisch } 4543a691b28SClemens Ladisch 4553a691b28SClemens Ladisch static int isight_create_pcm(struct isight *isight) 4563a691b28SClemens Ladisch { 4573a691b28SClemens Ladisch static struct snd_pcm_ops ops = { 4583a691b28SClemens Ladisch .open = isight_open, 4593a691b28SClemens Ladisch .close = isight_close, 4603a691b28SClemens Ladisch .ioctl = snd_pcm_lib_ioctl, 4613a691b28SClemens Ladisch .hw_params = isight_hw_params, 4623a691b28SClemens Ladisch .hw_free = isight_hw_free, 4633a691b28SClemens Ladisch .prepare = isight_prepare, 4643a691b28SClemens Ladisch .trigger = isight_trigger, 4653a691b28SClemens Ladisch .pointer = isight_pointer, 4663a691b28SClemens Ladisch .page = snd_pcm_lib_get_vmalloc_page, 4673a691b28SClemens Ladisch .mmap = snd_pcm_lib_mmap_vmalloc, 4683a691b28SClemens Ladisch }; 4693a691b28SClemens Ladisch struct snd_pcm *pcm; 4703a691b28SClemens Ladisch int err; 4713a691b28SClemens Ladisch 4723a691b28SClemens Ladisch err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm); 4733a691b28SClemens Ladisch if (err < 0) 4743a691b28SClemens Ladisch return err; 4753a691b28SClemens Ladisch pcm->private_data = isight; 4763a691b28SClemens Ladisch strcpy(pcm->name, "iSight"); 4773a691b28SClemens Ladisch isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 4783a691b28SClemens Ladisch isight->pcm->ops = &ops; 4793a691b28SClemens Ladisch 4803a691b28SClemens Ladisch return 0; 4813a691b28SClemens Ladisch } 4823a691b28SClemens Ladisch 4833a691b28SClemens Ladisch static int isight_gain_info(struct snd_kcontrol *ctl, 4843a691b28SClemens Ladisch struct snd_ctl_elem_info *info) 4853a691b28SClemens Ladisch { 4863a691b28SClemens Ladisch struct isight *isight = ctl->private_data; 4873a691b28SClemens Ladisch 4883a691b28SClemens Ladisch info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4893a691b28SClemens Ladisch info->count = 1; 4903a691b28SClemens Ladisch info->value.integer.min = isight->gain_min; 4913a691b28SClemens Ladisch info->value.integer.max = isight->gain_max; 4923a691b28SClemens Ladisch 4933a691b28SClemens Ladisch return 0; 4943a691b28SClemens Ladisch } 4953a691b28SClemens Ladisch 4963a691b28SClemens Ladisch static int isight_gain_get(struct snd_kcontrol *ctl, 4973a691b28SClemens Ladisch struct snd_ctl_elem_value *value) 4983a691b28SClemens Ladisch { 4993a691b28SClemens Ladisch struct isight *isight = ctl->private_data; 5003a691b28SClemens Ladisch __be32 gain; 5013a691b28SClemens Ladisch int err; 5023a691b28SClemens Ladisch 503ac34dad2SStefan Richter err = reg_read(isight, REG_GAIN, &gain); 5043a691b28SClemens Ladisch if (err < 0) 5053a691b28SClemens Ladisch return err; 5063a691b28SClemens Ladisch 5073a691b28SClemens Ladisch value->value.integer.value[0] = (s32)be32_to_cpu(gain); 5083a691b28SClemens Ladisch 5093a691b28SClemens Ladisch return 0; 5103a691b28SClemens Ladisch } 5113a691b28SClemens Ladisch 5123a691b28SClemens Ladisch static int isight_gain_put(struct snd_kcontrol *ctl, 5133a691b28SClemens Ladisch struct snd_ctl_elem_value *value) 5143a691b28SClemens Ladisch { 5153a691b28SClemens Ladisch struct isight *isight = ctl->private_data; 5163a691b28SClemens Ladisch 5173a691b28SClemens Ladisch if (value->value.integer.value[0] < isight->gain_min || 5183a691b28SClemens Ladisch value->value.integer.value[0] > isight->gain_max) 5193a691b28SClemens Ladisch return -EINVAL; 5203a691b28SClemens Ladisch 521ac34dad2SStefan Richter return reg_write(isight, REG_GAIN, 522ac34dad2SStefan Richter cpu_to_be32(value->value.integer.value[0])); 5233a691b28SClemens Ladisch } 5243a691b28SClemens Ladisch 5253a691b28SClemens Ladisch static int isight_mute_get(struct snd_kcontrol *ctl, 5263a691b28SClemens Ladisch struct snd_ctl_elem_value *value) 5273a691b28SClemens Ladisch { 5283a691b28SClemens Ladisch struct isight *isight = ctl->private_data; 5293a691b28SClemens Ladisch __be32 mute; 5303a691b28SClemens Ladisch int err; 5313a691b28SClemens Ladisch 532ac34dad2SStefan Richter err = reg_read(isight, REG_MUTE, &mute); 5333a691b28SClemens Ladisch if (err < 0) 5343a691b28SClemens Ladisch return err; 5353a691b28SClemens Ladisch 5363a691b28SClemens Ladisch value->value.integer.value[0] = !mute; 5373a691b28SClemens Ladisch 5383a691b28SClemens Ladisch return 0; 5393a691b28SClemens Ladisch } 5403a691b28SClemens Ladisch 5413a691b28SClemens Ladisch static int isight_mute_put(struct snd_kcontrol *ctl, 5423a691b28SClemens Ladisch struct snd_ctl_elem_value *value) 5433a691b28SClemens Ladisch { 5443a691b28SClemens Ladisch struct isight *isight = ctl->private_data; 5453a691b28SClemens Ladisch 546ac34dad2SStefan Richter return reg_write(isight, REG_MUTE, 547ac34dad2SStefan Richter (__force __be32)!value->value.integer.value[0]); 5483a691b28SClemens Ladisch } 5493a691b28SClemens Ladisch 5503a691b28SClemens Ladisch static int isight_create_mixer(struct isight *isight) 5513a691b28SClemens Ladisch { 5523a691b28SClemens Ladisch static const struct snd_kcontrol_new gain_control = { 5533a691b28SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5543a691b28SClemens Ladisch .name = "Mic Capture Volume", 5553a691b28SClemens Ladisch .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 5563a691b28SClemens Ladisch SNDRV_CTL_ELEM_ACCESS_TLV_READ, 5573a691b28SClemens Ladisch .info = isight_gain_info, 5583a691b28SClemens Ladisch .get = isight_gain_get, 5593a691b28SClemens Ladisch .put = isight_gain_put, 5603a691b28SClemens Ladisch }; 5613a691b28SClemens Ladisch static const struct snd_kcontrol_new mute_control = { 5623a691b28SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5633a691b28SClemens Ladisch .name = "Mic Capture Switch", 5643a691b28SClemens Ladisch .info = snd_ctl_boolean_mono_info, 5653a691b28SClemens Ladisch .get = isight_mute_get, 5663a691b28SClemens Ladisch .put = isight_mute_put, 5673a691b28SClemens Ladisch }; 5683a691b28SClemens Ladisch __be32 value; 5693a691b28SClemens Ladisch struct snd_kcontrol *ctl; 5703a691b28SClemens Ladisch int err; 5713a691b28SClemens Ladisch 572ac34dad2SStefan Richter err = reg_read(isight, REG_GAIN_RAW_START, &value); 5733a691b28SClemens Ladisch if (err < 0) 5743a691b28SClemens Ladisch return err; 5753a691b28SClemens Ladisch isight->gain_min = be32_to_cpu(value); 5763a691b28SClemens Ladisch 577ac34dad2SStefan Richter err = reg_read(isight, REG_GAIN_RAW_END, &value); 5783a691b28SClemens Ladisch if (err < 0) 5793a691b28SClemens Ladisch return err; 5803a691b28SClemens Ladisch isight->gain_max = be32_to_cpu(value); 5813a691b28SClemens Ladisch 5823a691b28SClemens Ladisch isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX; 5833a691b28SClemens Ladisch isight->gain_tlv[1] = 2 * sizeof(unsigned int); 584ac34dad2SStefan Richter 585ac34dad2SStefan Richter err = reg_read(isight, REG_GAIN_DB_START, &value); 5863a691b28SClemens Ladisch if (err < 0) 5873a691b28SClemens Ladisch return err; 5883a691b28SClemens Ladisch isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100; 589ac34dad2SStefan Richter 590ac34dad2SStefan Richter err = reg_read(isight, REG_GAIN_DB_END, &value); 5913a691b28SClemens Ladisch if (err < 0) 5923a691b28SClemens Ladisch return err; 5933a691b28SClemens Ladisch isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100; 5943a691b28SClemens Ladisch 5953a691b28SClemens Ladisch ctl = snd_ctl_new1(&gain_control, isight); 5963a691b28SClemens Ladisch if (ctl) 5973a691b28SClemens Ladisch ctl->tlv.p = isight->gain_tlv; 5983a691b28SClemens Ladisch err = snd_ctl_add(isight->card, ctl); 5993a691b28SClemens Ladisch if (err < 0) 6003a691b28SClemens Ladisch return err; 6013a691b28SClemens Ladisch 6023a691b28SClemens Ladisch err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight)); 6033a691b28SClemens Ladisch if (err < 0) 6043a691b28SClemens Ladisch return err; 6053a691b28SClemens Ladisch 6063a691b28SClemens Ladisch return 0; 6073a691b28SClemens Ladisch } 6083a691b28SClemens Ladisch 6093a691b28SClemens Ladisch static void isight_card_free(struct snd_card *card) 6103a691b28SClemens Ladisch { 6113a691b28SClemens Ladisch struct isight *isight = card->private_data; 6123a691b28SClemens Ladisch 6133a691b28SClemens Ladisch fw_iso_resources_destroy(&isight->resources); 6143a691b28SClemens Ladisch fw_unit_put(isight->unit); 6153a691b28SClemens Ladisch fw_device_put(isight->device); 6163a691b28SClemens Ladisch mutex_destroy(&isight->mutex); 6173a691b28SClemens Ladisch } 6183a691b28SClemens Ladisch 6193a691b28SClemens Ladisch static u64 get_unit_base(struct fw_unit *unit) 6203a691b28SClemens Ladisch { 6213a691b28SClemens Ladisch struct fw_csr_iterator i; 6223a691b28SClemens Ladisch int key, value; 6233a691b28SClemens Ladisch 6243a691b28SClemens Ladisch fw_csr_iterator_init(&i, unit->directory); 6253a691b28SClemens Ladisch while (fw_csr_iterator_next(&i, &key, &value)) 6263a691b28SClemens Ladisch if (key == CSR_OFFSET) 6273a691b28SClemens Ladisch return CSR_REGISTER_BASE + value * 4; 6283a691b28SClemens Ladisch return 0; 6293a691b28SClemens Ladisch } 6303a691b28SClemens Ladisch 6313a691b28SClemens Ladisch static int isight_probe(struct device *unit_dev) 6323a691b28SClemens Ladisch { 6333a691b28SClemens Ladisch struct fw_unit *unit = fw_unit(unit_dev); 6343a691b28SClemens Ladisch struct fw_device *fw_dev = fw_parent_device(unit); 6353a691b28SClemens Ladisch struct snd_card *card; 6363a691b28SClemens Ladisch struct isight *isight; 6373a691b28SClemens Ladisch int err; 6383a691b28SClemens Ladisch 6393a691b28SClemens Ladisch err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*isight), &card); 6403a691b28SClemens Ladisch if (err < 0) 6413a691b28SClemens Ladisch return err; 6423a691b28SClemens Ladisch snd_card_set_dev(card, unit_dev); 6433a691b28SClemens Ladisch 6443a691b28SClemens Ladisch isight = card->private_data; 6453a691b28SClemens Ladisch isight->card = card; 6463a691b28SClemens Ladisch mutex_init(&isight->mutex); 6473a691b28SClemens Ladisch isight->unit = fw_unit_get(unit); 6483a691b28SClemens Ladisch isight->device = fw_device_get(fw_dev); 6493a691b28SClemens Ladisch isight->audio_base = get_unit_base(unit); 6503a691b28SClemens Ladisch if (!isight->audio_base) { 6513a691b28SClemens Ladisch dev_err(&unit->device, "audio unit base not found\n"); 6523a691b28SClemens Ladisch err = -ENXIO; 6533a691b28SClemens Ladisch goto err_unit; 6543a691b28SClemens Ladisch } 6553a691b28SClemens Ladisch fw_iso_resources_init(&isight->resources, unit); 6563a691b28SClemens Ladisch 6573a691b28SClemens Ladisch card->private_free = isight_card_free; 6583a691b28SClemens Ladisch 6593a691b28SClemens Ladisch strcpy(card->driver, "iSight"); 6603a691b28SClemens Ladisch strcpy(card->shortname, "Apple iSight"); 6613a691b28SClemens Ladisch snprintf(card->longname, sizeof(card->longname), 6623a691b28SClemens Ladisch "Apple iSight (GUID %08x%08x) at %s, S%d", 6633a691b28SClemens Ladisch fw_dev->config_rom[3], fw_dev->config_rom[4], 6643a691b28SClemens Ladisch dev_name(&unit->device), 100 << fw_dev->max_speed); 6653a691b28SClemens Ladisch strcpy(card->mixername, "iSight"); 6663a691b28SClemens Ladisch 6673a691b28SClemens Ladisch err = isight_create_pcm(isight); 6683a691b28SClemens Ladisch if (err < 0) 6693a691b28SClemens Ladisch goto error; 6703a691b28SClemens Ladisch 6713a691b28SClemens Ladisch err = isight_create_mixer(isight); 6723a691b28SClemens Ladisch if (err < 0) 6733a691b28SClemens Ladisch goto error; 6743a691b28SClemens Ladisch 6753a691b28SClemens Ladisch err = snd_card_register(card); 6763a691b28SClemens Ladisch if (err < 0) 6773a691b28SClemens Ladisch goto error; 6783a691b28SClemens Ladisch 6793a691b28SClemens Ladisch dev_set_drvdata(unit_dev, isight); 6803a691b28SClemens Ladisch 6813a691b28SClemens Ladisch return 0; 6823a691b28SClemens Ladisch 6833a691b28SClemens Ladisch err_unit: 6843a691b28SClemens Ladisch fw_unit_put(isight->unit); 6853a691b28SClemens Ladisch fw_device_put(isight->device); 6863a691b28SClemens Ladisch mutex_destroy(&isight->mutex); 6873a691b28SClemens Ladisch error: 6883a691b28SClemens Ladisch snd_card_free(card); 6893a691b28SClemens Ladisch return err; 6903a691b28SClemens Ladisch } 6913a691b28SClemens Ladisch 6923a691b28SClemens Ladisch static int isight_remove(struct device *dev) 6933a691b28SClemens Ladisch { 6943a691b28SClemens Ladisch struct isight *isight = dev_get_drvdata(dev); 6953a691b28SClemens Ladisch 6963a691b28SClemens Ladisch isight_pcm_abort(isight); 697f3f7c183SClemens Ladisch 698aee70400SClemens Ladisch snd_card_disconnect(isight->card); 699f3f7c183SClemens Ladisch 700f3f7c183SClemens Ladisch mutex_lock(&isight->mutex); 7013a691b28SClemens Ladisch isight_stop_streaming(isight); 7023a691b28SClemens Ladisch mutex_unlock(&isight->mutex); 7033a691b28SClemens Ladisch 7043a691b28SClemens Ladisch snd_card_free_when_closed(isight->card); 7053a691b28SClemens Ladisch 7063a691b28SClemens Ladisch return 0; 7073a691b28SClemens Ladisch } 7083a691b28SClemens Ladisch 7093a691b28SClemens Ladisch static void isight_bus_reset(struct fw_unit *unit) 7103a691b28SClemens Ladisch { 7113a691b28SClemens Ladisch struct isight *isight = dev_get_drvdata(&unit->device); 7123a691b28SClemens Ladisch 7133a691b28SClemens Ladisch if (fw_iso_resources_update(&isight->resources) < 0) { 7143a691b28SClemens Ladisch isight_pcm_abort(isight); 715f3f7c183SClemens Ladisch 716f3f7c183SClemens Ladisch mutex_lock(&isight->mutex); 7173a691b28SClemens Ladisch isight_stop_streaming(isight); 7183a691b28SClemens Ladisch mutex_unlock(&isight->mutex); 7193a691b28SClemens Ladisch } 720f3f7c183SClemens Ladisch } 7213a691b28SClemens Ladisch 7223a691b28SClemens Ladisch static const struct ieee1394_device_id isight_id_table[] = { 7233a691b28SClemens Ladisch { 7243a691b28SClemens Ladisch .match_flags = IEEE1394_MATCH_SPECIFIER_ID | 7253a691b28SClemens Ladisch IEEE1394_MATCH_VERSION, 7263a691b28SClemens Ladisch .specifier_id = OUI_APPLE, 7273a691b28SClemens Ladisch .version = SW_ISIGHT_AUDIO, 7283a691b28SClemens Ladisch }, 7293a691b28SClemens Ladisch { } 7303a691b28SClemens Ladisch }; 7313a691b28SClemens Ladisch MODULE_DEVICE_TABLE(ieee1394, isight_id_table); 7323a691b28SClemens Ladisch 7333a691b28SClemens Ladisch static struct fw_driver isight_driver = { 7343a691b28SClemens Ladisch .driver = { 7353a691b28SClemens Ladisch .owner = THIS_MODULE, 7363a691b28SClemens Ladisch .name = KBUILD_MODNAME, 7373a691b28SClemens Ladisch .bus = &fw_bus_type, 7383a691b28SClemens Ladisch .probe = isight_probe, 7393a691b28SClemens Ladisch .remove = isight_remove, 7403a691b28SClemens Ladisch }, 7413a691b28SClemens Ladisch .update = isight_bus_reset, 7423a691b28SClemens Ladisch .id_table = isight_id_table, 7433a691b28SClemens Ladisch }; 7443a691b28SClemens Ladisch 7453a691b28SClemens Ladisch static int __init alsa_isight_init(void) 7463a691b28SClemens Ladisch { 7473a691b28SClemens Ladisch return driver_register(&isight_driver.driver); 7483a691b28SClemens Ladisch } 7493a691b28SClemens Ladisch 7503a691b28SClemens Ladisch static void __exit alsa_isight_exit(void) 7513a691b28SClemens Ladisch { 7523a691b28SClemens Ladisch driver_unregister(&isight_driver.driver); 7533a691b28SClemens Ladisch } 7543a691b28SClemens Ladisch 7553a691b28SClemens Ladisch module_init(alsa_isight_init); 7563a691b28SClemens Ladisch module_exit(alsa_isight_exit); 757