ALSA: pcxhr: Add LTC support
authorMarkus Bollinger <bollinger@digigram.com>
Wed, 20 Jun 2012 06:34:40 +0000 (08:34 +0200)
committerTakashi Iwai <tiwai@suse.de>
Wed, 20 Jun 2012 06:35:37 +0000 (08:35 +0200)
add LTC (linear timecode) read function via proc interface to the pcxhr driver

Signed-off-by: Markus Bollinger <bollinger@digigram.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/pcxhr/pcxhr.c
sound/pci/pcxhr/pcxhr.h
sound/pci/pcxhr/pcxhr_core.c
sound/pci/pcxhr/pcxhr_core.h
sound/pci/pcxhr/pcxhr_mix22.c
sound/pci/pcxhr/pcxhr_mix22.h

index 0435f45e95132d59583a09c9d72a25f19d536408..e3ac1f768ff6c6065768d3d62dd628f5b041aac6 100644 (file)
@@ -1368,6 +1368,67 @@ static void pcxhr_proc_gpo_write(struct snd_info_entry *entry,
        }
 }
 
+/* Access to the results of the CMD_GET_TIME_CODE RMH */
+#define TIME_CODE_VALID_MASK   0x00800000
+#define TIME_CODE_NEW_MASK     0x00400000
+#define TIME_CODE_BACK_MASK    0x00200000
+#define TIME_CODE_WAIT_MASK    0x00100000
+
+/* Values for the CMD_MANAGE_SIGNAL RMH */
+#define MANAGE_SIGNAL_TIME_CODE        0x01
+#define MANAGE_SIGNAL_MIDI     0x02
+
+/* linear time code read proc*/
+static void pcxhr_proc_ltc(struct snd_info_entry *entry,
+                          struct snd_info_buffer *buffer)
+{
+       struct snd_pcxhr *chip = entry->private_data;
+       struct pcxhr_mgr *mgr = chip->mgr;
+       struct pcxhr_rmh rmh;
+       unsigned int ltcHrs, ltcMin, ltcSec, ltcFrm;
+       int err;
+       /* commands available when embedded DSP is running */
+       if (!(mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX))) {
+               snd_iprintf(buffer, "no firmware loaded\n");
+               return;
+       }
+       if (!mgr->capture_ltc) {
+               pcxhr_init_rmh(&rmh, CMD_MANAGE_SIGNAL);
+               rmh.cmd[0] |= MANAGE_SIGNAL_TIME_CODE;
+               err = pcxhr_send_msg(mgr, &rmh);
+               if (err) {
+                       snd_iprintf(buffer, "ltc not activated (%d)\n", err);
+                       return;
+               }
+               if (mgr->is_hr_stereo)
+                       hr222_manage_timecode(mgr, 1);
+               else
+                       pcxhr_write_io_num_reg_cont(mgr, REG_CONT_VALSMPTE,
+                                                   REG_CONT_VALSMPTE, NULL);
+               mgr->capture_ltc = 1;
+       }
+       pcxhr_init_rmh(&rmh, CMD_GET_TIME_CODE);
+       err = pcxhr_send_msg(mgr, &rmh);
+       if (err) {
+               snd_iprintf(buffer, "ltc read error (err=%d)\n", err);
+               return ;
+       }
+       ltcHrs = 10*((rmh.stat[0] >> 8) & 0x3) + (rmh.stat[0] & 0xf);
+       ltcMin = 10*((rmh.stat[1] >> 16) & 0x7) + ((rmh.stat[1] >> 8) & 0xf);
+       ltcSec = 10*(rmh.stat[1] & 0x7) + ((rmh.stat[2] >> 16) & 0xf);
+       ltcFrm = 10*((rmh.stat[2] >> 8) & 0x3) + (rmh.stat[2] & 0xf);
+
+       snd_iprintf(buffer, "timecode: %02u:%02u:%02u-%02u\n",
+                           ltcHrs, ltcMin, ltcSec, ltcFrm);
+       snd_iprintf(buffer, "raw: 0x%04x%06x%06x\n", rmh.stat[0] & 0x00ffff,
+                           rmh.stat[1] & 0xffffff, rmh.stat[2] & 0xffffff);
+       /*snd_iprintf(buffer, "dsp ref time: 0x%06x%06x\n",
+                           rmh.stat[3] & 0xffffff, rmh.stat[4] & 0xffffff);*/
+       if (!(rmh.stat[0] & TIME_CODE_VALID_MASK)) {
+               snd_iprintf(buffer, "warning: linear timecode not valid\n");
+       }
+}
+
 static void __devinit pcxhr_proc_init(struct snd_pcxhr *chip)
 {
        struct snd_info_entry *entry;
@@ -1383,6 +1444,8 @@ static void __devinit pcxhr_proc_init(struct snd_pcxhr *chip)
                entry->c.text.write = pcxhr_proc_gpo_write;
                entry->mode |= S_IWUSR;
        }
+       if (!snd_card_proc_new(chip->card, "ltc", &entry))
+               snd_info_set_text_ops(entry, chip, pcxhr_proc_ltc);
 }
 /* end of proc interface */
 
index bda776c498845b7524ba16bdfc043010d07daca3..a4c602c451738bb541b2c1ddf20195d429c99000 100644 (file)
@@ -103,6 +103,7 @@ struct pcxhr_mgr {
        unsigned int board_has_mic:1; /* if 1 the board has microphone input */
        unsigned int board_aes_in_192k:1;/* if 1 the aes input plugs do support 192kHz */
        unsigned int mono_capture:1; /* if 1 the board does mono capture */
+       unsigned int capture_ltc:1; /* if 1 the board captures LTC input */
 
        struct snd_dma_buffer hostport;
 
index 841703b5c52ad3b14dbeb0664326ac325ccabbae..b33db1e006e704f4c0c6d3806ae53a25537f0ad0 100644 (file)
@@ -504,6 +504,8 @@ static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
 [CMD_FORMAT_STREAM_IN] =               { 0x870000, 0, RMH_SSIZE_FIXED },
 [CMD_STREAM_SAMPLE_COUNT] =            { 0x902000, 2, RMH_SSIZE_FIXED },
 [CMD_AUDIO_LEVEL_ADJUST] =             { 0xc22000, 0, RMH_SSIZE_FIXED },
+[CMD_GET_TIME_CODE] =                  { 0x060000, 5, RMH_SSIZE_FIXED },
+[CMD_MANAGE_SIGNAL] =                  { 0x0f0000, 0, RMH_SSIZE_FIXED },
 };
 
 #ifdef CONFIG_SND_DEBUG_VERBOSE
@@ -533,6 +535,8 @@ static char* cmd_names[] = {
 [CMD_FORMAT_STREAM_IN] =               "CMD_FORMAT_STREAM_IN",
 [CMD_STREAM_SAMPLE_COUNT] =            "CMD_STREAM_SAMPLE_COUNT",
 [CMD_AUDIO_LEVEL_ADJUST] =             "CMD_AUDIO_LEVEL_ADJUST",
+[CMD_GET_TIME_CODE] =                  "CMD_GET_TIME_CODE",
+[CMD_MANAGE_SIGNAL] =                  "CMD_MANAGE_SIGNAL",
 };
 #endif
 
index be0173796cdb5b22022ea2cd64dbb368a4d4c60d..a81ab6b811e727a8f18820fed1cd3c5c1f2f3f2f 100644 (file)
@@ -79,6 +79,8 @@ enum {
        CMD_FORMAT_STREAM_IN,           /* cmd_len >= 4 stat_len = 0 */
        CMD_STREAM_SAMPLE_COUNT,        /* cmd_len = 2  stat_len = (2 * nb_stream) */
        CMD_AUDIO_LEVEL_ADJUST,         /* cmd_len = 3  stat_len = 0 */
+       CMD_GET_TIME_CODE,              /* cmd_len = 1  stat_len = 5 */
+       CMD_MANAGE_SIGNAL,              /* cmd_len = 1  stat_len = 0 */
        CMD_LAST_INDEX
 };
 
@@ -116,7 +118,7 @@ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh);
 #define IO_NUM_REG_OUT_ANA_LEVEL       20
 #define IO_NUM_REG_IN_ANA_LEVEL                21
 
-
+#define REG_CONT_VALSMPTE              0x000800
 #define REG_CONT_UNMUTE_INPUTS         0x020000
 
 /* parameters used with register IO_NUM_REG_STATUS */
index 1cb82c0a9cb3f4942b258464af2163aa7a0f5064..84fe57626ebaa256a8581ea0fe345c453fddd421 100644 (file)
@@ -53,6 +53,7 @@
 #define PCXHR_DSP_RESET_DSP    0x01
 #define PCXHR_DSP_RESET_MUTE   0x02
 #define PCXHR_DSP_RESET_CODEC  0x08
+#define PCXHR_DSP_RESET_SMPTE  0x10
 #define PCXHR_DSP_RESET_GPO_OFFSET     5
 #define PCXHR_DSP_RESET_GPO_MASK       0x60
 
@@ -527,6 +528,16 @@ int hr222_write_gpo(struct pcxhr_mgr *mgr, int value)
        return 0;
 }
 
+int hr222_manage_timecode(struct pcxhr_mgr *mgr, int enable)
+{
+       if (enable)
+               mgr->dsp_reset |= PCXHR_DSP_RESET_SMPTE;
+       else
+               mgr->dsp_reset &= ~PCXHR_DSP_RESET_SMPTE;
+
+       PCXHR_OUTPB(mgr, PCXHR_DSP_RESET, mgr->dsp_reset);
+       return 0;
+}
 
 int hr222_update_analog_audio_level(struct snd_pcxhr *chip,
                                    int is_capture, int channel)
index 5a37a0007e8fd071cc430ad67bfe5853bfef15c6..5971b9933f418e2a293b367098d00d00938dc547 100644 (file)
@@ -34,6 +34,7 @@ int hr222_get_external_clock(struct pcxhr_mgr *mgr,
 
 int hr222_read_gpio(struct pcxhr_mgr *mgr, int is_gpi, int *value);
 int hr222_write_gpo(struct pcxhr_mgr *mgr, int value);
+int hr222_manage_timecode(struct pcxhr_mgr *mgr, int enable);
 
 #define HR222_LINE_PLAYBACK_LEVEL_MIN          0       /* -25.5 dB */
 #define HR222_LINE_PLAYBACK_ZERO_LEVEL         51      /* 0.0 dB */