| /* |
| * ff-proc.c - a part of driver for RME Fireface series |
| * |
| * Copyright (c) 2015-2017 Takashi Sakamoto |
| * |
| * Licensed under the terms of the GNU General Public License, version 2. |
| */ |
| |
| #include "./ff.h" |
| |
| static void proc_dump_clock_config(struct snd_info_entry *entry, |
| struct snd_info_buffer *buffer) |
| { |
| struct snd_ff *ff = entry->private_data; |
| __le32 reg; |
| u32 data; |
| unsigned int rate; |
| const char *src; |
| int err; |
| |
| err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, |
| SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); |
| if (err < 0) |
| return; |
| |
| data = le32_to_cpu(reg); |
| |
| snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", |
| (data & 0x20) ? "Professional" : "Consumer", |
| (data & 0x40) ? "on" : "off"); |
| |
| snd_iprintf(buffer, "Optical output interface format: %s\n", |
| ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT"); |
| |
| snd_iprintf(buffer, "Word output single speed: %s\n", |
| ((data >> 8) & 0x20) ? "on" : "off"); |
| |
| snd_iprintf(buffer, "S/PDIF input interface: %s\n", |
| ((data >> 8) & 0x02) ? "Optical" : "Coaxial"); |
| |
| switch ((data >> 1) & 0x03) { |
| case 0x01: |
| rate = 32000; |
| break; |
| case 0x00: |
| rate = 44100; |
| break; |
| case 0x03: |
| rate = 48000; |
| break; |
| case 0x02: |
| default: |
| return; |
| } |
| |
| if (data & 0x08) |
| rate *= 2; |
| else if (data & 0x10) |
| rate *= 4; |
| |
| snd_iprintf(buffer, "Sampling rate: %d\n", rate); |
| |
| if (data & 0x01) { |
| src = "Internal"; |
| } else { |
| switch ((data >> 10) & 0x07) { |
| case 0x00: |
| src = "ADAT1"; |
| break; |
| case 0x01: |
| src = "ADAT2"; |
| break; |
| case 0x03: |
| src = "S/PDIF"; |
| break; |
| case 0x04: |
| src = "Word"; |
| break; |
| case 0x05: |
| src = "LTC"; |
| break; |
| default: |
| return; |
| } |
| } |
| |
| snd_iprintf(buffer, "Sync to clock source: %s\n", src); |
| } |
| |
| static void proc_dump_sync_status(struct snd_info_entry *entry, |
| struct snd_info_buffer *buffer) |
| { |
| struct snd_ff *ff = entry->private_data; |
| __le32 reg; |
| u32 data; |
| int err; |
| |
| err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, |
| SND_FF_REG_SYNC_STATUS, ®, sizeof(reg), 0); |
| if (err < 0) |
| return; |
| |
| data = le32_to_cpu(reg); |
| |
| snd_iprintf(buffer, "External source detection:\n"); |
| |
| snd_iprintf(buffer, "Word Clock:"); |
| if ((data >> 24) & 0x20) { |
| if ((data >> 24) & 0x40) |
| snd_iprintf(buffer, "sync\n"); |
| else |
| snd_iprintf(buffer, "lock\n"); |
| } else { |
| snd_iprintf(buffer, "none\n"); |
| } |
| |
| snd_iprintf(buffer, "S/PDIF:"); |
| if ((data >> 16) & 0x10) { |
| if ((data >> 16) & 0x04) |
| snd_iprintf(buffer, "sync\n"); |
| else |
| snd_iprintf(buffer, "lock\n"); |
| } else { |
| snd_iprintf(buffer, "none\n"); |
| } |
| |
| snd_iprintf(buffer, "ADAT1:"); |
| if ((data >> 8) & 0x04) { |
| if ((data >> 8) & 0x10) |
| snd_iprintf(buffer, "sync\n"); |
| else |
| snd_iprintf(buffer, "lock\n"); |
| } else { |
| snd_iprintf(buffer, "none\n"); |
| } |
| |
| snd_iprintf(buffer, "ADAT2:"); |
| if ((data >> 8) & 0x08) { |
| if ((data >> 8) & 0x20) |
| snd_iprintf(buffer, "sync\n"); |
| else |
| snd_iprintf(buffer, "lock\n"); |
| } else { |
| snd_iprintf(buffer, "none\n"); |
| } |
| |
| snd_iprintf(buffer, "\nUsed external source:\n"); |
| |
| if (((data >> 22) & 0x07) == 0x07) { |
| snd_iprintf(buffer, "None\n"); |
| } else { |
| switch ((data >> 22) & 0x07) { |
| case 0x00: |
| snd_iprintf(buffer, "ADAT1:"); |
| break; |
| case 0x01: |
| snd_iprintf(buffer, "ADAT2:"); |
| break; |
| case 0x03: |
| snd_iprintf(buffer, "S/PDIF:"); |
| break; |
| case 0x04: |
| snd_iprintf(buffer, "Word:"); |
| break; |
| case 0x07: |
| snd_iprintf(buffer, "Nothing:"); |
| break; |
| case 0x02: |
| case 0x05: |
| case 0x06: |
| default: |
| snd_iprintf(buffer, "unknown:"); |
| break; |
| } |
| |
| if ((data >> 25) & 0x07) { |
| switch ((data >> 25) & 0x07) { |
| case 0x01: |
| snd_iprintf(buffer, "32000\n"); |
| break; |
| case 0x02: |
| snd_iprintf(buffer, "44100\n"); |
| break; |
| case 0x03: |
| snd_iprintf(buffer, "48000\n"); |
| break; |
| case 0x04: |
| snd_iprintf(buffer, "64000\n"); |
| break; |
| case 0x05: |
| snd_iprintf(buffer, "88200\n"); |
| break; |
| case 0x06: |
| snd_iprintf(buffer, "96000\n"); |
| break; |
| case 0x07: |
| snd_iprintf(buffer, "128000\n"); |
| break; |
| case 0x08: |
| snd_iprintf(buffer, "176400\n"); |
| break; |
| case 0x09: |
| snd_iprintf(buffer, "192000\n"); |
| break; |
| case 0x00: |
| snd_iprintf(buffer, "unknown\n"); |
| break; |
| } |
| } |
| } |
| |
| snd_iprintf(buffer, "Multiplied:"); |
| snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); |
| } |
| |
| static void add_node(struct snd_ff *ff, struct snd_info_entry *root, |
| const char *name, |
| void (*op)(struct snd_info_entry *e, |
| struct snd_info_buffer *b)) |
| { |
| struct snd_info_entry *entry; |
| |
| entry = snd_info_create_card_entry(ff->card, name, root); |
| if (entry == NULL) |
| return; |
| |
| snd_info_set_text_ops(entry, ff, op); |
| if (snd_info_register(entry) < 0) |
| snd_info_free_entry(entry); |
| } |
| |
| void snd_ff_proc_init(struct snd_ff *ff) |
| { |
| struct snd_info_entry *root; |
| |
| /* |
| * All nodes are automatically removed at snd_card_disconnect(), |
| * by following to link list. |
| */ |
| root = snd_info_create_card_entry(ff->card, "firewire", |
| ff->card->proc_root); |
| if (root == NULL) |
| return; |
| root->mode = S_IFDIR | 0555; |
| if (snd_info_register(root) < 0) { |
| snd_info_free_entry(root); |
| return; |
| } |
| |
| add_node(ff, root, "clock-config", proc_dump_clock_config); |
| add_node(ff, root, "sync-status", proc_dump_sync_status); |
| } |