From 06aefaee5a24acb0b2bcf9871534f3c0ad428a30 Mon Sep 17 00:00:00 2001 From: Philippe Bekaert Date: Wed, 30 Mar 2022 21:13:43 +0200 Subject: [PATCH] TCO updates and status polling fix --- Makefile | 8 + doc/controls.md | 7 +- sound/pci/hdsp/hdspe/hdspe.h | 52 +++- sound/pci/hdsp/hdspe/hdspe_control.c | 23 +- sound/pci/hdsp/hdspe/hdspe_control.h | 22 +- sound/pci/hdsp/hdspe/hdspe_core.c | 2 + sound/pci/hdsp/hdspe/hdspe_core.h | 7 +- sound/pci/hdsp/hdspe/hdspe_midi.c | 11 +- sound/pci/hdsp/hdspe/hdspe_tco.c | 340 +++++++++++++++++++++++++-- sound/pci/hdsp/hdspe/snd-hdspe.mod.c | 131 +++++++++++ 10 files changed, 557 insertions(+), 46 deletions(-) create mode 100644 sound/pci/hdsp/hdspe/snd-hdspe.mod.c diff --git a/Makefile b/Makefile index 94ce272..50298ba 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,14 @@ insert: default remove: rmmod snd-hdspe +install: default + -rmmod snd-hdspm + -ln -s $(pwd) /usr/src/alsa-hdspe-0.0 + dkms install alsa-hdspe/0.0 + +uninstall: + dkms remove alsa-hdspe/0.0 --all + list-controls: -rm asound.state alsactl -f asound.state store diff --git a/doc/controls.md b/doc/controls.md index 73391f0..fb2771b 100644 --- a/doc/controls.md +++ b/doc/controls.md @@ -81,6 +81,7 @@ TCO controls | Interface | Name | Access | Value Type | Description | | :- | :- | :- | :- | :- | +| CARD | TCO Firmware | RO | Int | TCO module firmware version | | CARD | LTC In | RV | Int64 | Incoming LTC code - see below **LTC control** | | CARD | LTC In Drop Frame | RV | Bool | Whether incoming LTC is drop frame format or not | | CARD | LTC In Frame Rate | RV | Enum | Incoming **LTC frame rate**: 24, 25 or 30 fps | @@ -94,11 +95,13 @@ TCO controls | CARD | TCO Lock | RV | Bool | Whether or not the TCO is locked to LTC, Video or Word Clock | | CARD | TCO Pull | RW | Enum | Pull Up / Pull Down factor | | CARD | TCO Sync Source | RW | Enum | TCO preferred synchronisation source: LTC, Video or Word Clock | -| CARD | TCO Video Format | RV | Enum | Video format reference signal detected: PAL or NTSC blackburst | +| CARD | TCO Video Format | RV | Enum | Video format reference signal detected: PAL or NTSC blackburst | +| CARD | TCO Video Frame Rate | RV | Enum | Video frame rate detected (meaningful only if TCO firmware >= 11) | | CARD | TCO WordClk Conversion | RW | Enum | Word clock rate conversion 1:1, 44.1 -> 48 KHz, 48 -> 44.1 KHz | | CARD | TCO WordClk Term | RW | Bool | Whether or not to 75 Ohm terminate the word clock/video input BNC | | CARD | TCO WordClk Valid | RV | Bool | Whether or not a valid word clock signal is detected | -| CARD | TCO WordClk Speed | RV | Enum | Detected input word clock speed | +| CARD | TCO WordClk Speed | RV | Enum | Detected input word clock speed | +| CARD | TCO WorldClk Out Speed | RW | Enum | Output word clock speed | **LTC Control** diff --git a/sound/pci/hdsp/hdspe/hdspe.h b/sound/pci/hdsp/hdspe/hdspe.h index e2792a0..8548226 100644 --- a/sound/pci/hdsp/hdspe/hdspe.h +++ b/sound/pci/hdsp/hdspe/hdspe.h @@ -3,7 +3,19 @@ * @file hdspe.h * @brief RME HDSPe driver user space API. * + * Note: The definitions and structs defined in this header file are used + * within the driver as well as in IOCTLs. As they are used within the + * driver, they are up to date. However, the IOCTLs should be considered + * obsolete: the same information on the driver can be obtained via + * standard ALSA control mechanisms, except for the mixer and level + * meters at this time. To my knowledge (PhB), the only (public) application + * still using the IOCTL interface is hdspmixer. hdspeconf is fully based on + * the ALSA control mechanism. Do not use the IOCTLs for new development - + * inform Philippe.Bekaert@uhasselt.be in case you wouldn't be able to do + * without. + * * 20210728 ... 0813 - Philippe.Bekaert@uhasselt.be + * 20220329,30 - PhB : API version 3 (TCO related additions) * * Based on earlier work by Winfried Ritsch (IEM, 2003) and * Thomas Charbonnel (thomas@undata.org), @@ -21,7 +33,7 @@ * Structs returned by the HDSPe driver ioctls contain the API version with which the * kernel driver has been compiled. API users should check that version against * HDSPE_VERSION and take appropriate action in case versions differ. */ -#define HDSPE_VERSION 2 +#define HDSPE_VERSION 3 /* Maximum hardware input, software playback and hardware output * channels is 64 even on 56Mode you have 64playbacks to matrix. */ @@ -460,11 +472,41 @@ enum hdspe_video_format { }; #define HDSPE_VIDEO_FORMAT_NAME(i) \ - (i == HDSPE_VIDEO_FORMAT_NO_VIDEO ? "No Video" : \ + (i == HDSPE_VIDEO_FORMAT_NO_VIDEO ? "" : \ i == HDSPE_VIDEO_FORMAT_NTSC ? "NTSC" : \ i == HDSPE_VIDEO_FORMAT_PAL ? "PAL" : \ "???") +enum hdspe_video_fps { /* TCO firmware version 11 and above */ + HDSPE_VIDEO_FPS_NO_VIDEO =0, + HDSPE_VIDEO_FPS_23_98 =1, + HDSPE_VIDEO_FPS_24 =2, + HDSPE_VIDEO_FPS_25 =3, + HDSPE_VIDEO_FPS_29_97 =4, + HDSPE_VIDEO_FPS_30 =5, + HDSPE_VIDEO_FPS_47_95 =6, + HDSPE_VIDEO_FPS_48 =7, + HDSPE_VIDEO_FPS_50 =8, + HDSPE_VIDEO_FPS_59_94 =9, + HDSPE_VIDEO_FPS_60 =10, + HDSPE_VIDEO_FPS_COUNT =11, + HDSPE_VIDEO_FOS_FORCE_32BIT = 0xffffffff +}; + +#define HDSPE_VIDEO_FPS_NAME(i) \ + (i == HDSPE_VIDEO_FPS_NO_VIDEO ? "" : \ + i == HDSPE_VIDEO_FPS_23_98 ? "23.98" : \ + i == HDSPE_VIDEO_FPS_24 ? "24" : \ + i == HDSPE_VIDEO_FPS_25 ? "25" : \ + i == HDSPE_VIDEO_FPS_29_97 ? "29.97" : \ + i == HDSPE_VIDEO_FPS_30 ? "30" : \ + i == HDSPE_VIDEO_FPS_47_95 ? "47.95" : \ + i == HDSPE_VIDEO_FPS_48 ? "28" : \ + i == HDSPE_VIDEO_FPS_50 ? "50" : \ + i == HDSPE_VIDEO_FPS_59_94 ? "59.94" : \ + i == HDSPE_VIDEO_FPS_60 ? "60" : \ + "???") + enum hdspe_tco_source { HDSPE_TCO_SOURCE_WCK =0, HDSPE_TCO_SOURCE_VIDEO =1, @@ -612,6 +654,12 @@ struct hdspe_tco_status { // LTC output control enum hdspe_bool ltc_run; enum hdspe_bool ltc_flywheel; + + // HDSPE_VERSION 3: + uint32_t fw_version; + uint32_t fs_period_counter; + enum hdspe_video_fps video_in_fps; // if fw_version >= 11 + enum hdspe_speed wck_out_speed; }; #define SNDRV_HDSPE_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspe_tco_status) diff --git a/sound/pci/hdsp/hdspe/hdspe_control.c b/sound/pci/hdsp/hdspe/hdspe_control.c index f5726a7..36776df 100644 --- a/sound/pci/hdsp/hdspe/hdspe_control.c +++ b/sound/pci/hdsp/hdspe/hdspe_control.c @@ -3,7 +3,7 @@ * hdspe-control.c * @brief RME HDSPe sound card driver status and control interface. * - * 20210727,28,29,30,0908,09,10 - Philippe.Bekaert@uhasselt.be + * 20210727,28,29,30,0908,09,10,20220330 - Philippe.Bekaert@uhasselt.be * * Based on earlier work of the other MODULE_AUTHORS, * information kindly made available by RME (www.rme-audio.com), @@ -195,17 +195,20 @@ void hdspe_status_work(struct work_struct *work) if (hdspe->last_status_change_jiffies == 0) hdspe->last_status_change_jiffies = jiffies; - - if (changed || jiffies > hdspe->last_status_change_jiffies + 2*HZ) { - if (jiffies > hdspe->last_status_change_jiffies + 2*HZ) - dev_dbg(hdspe->card->dev, - "%s: polling timeout expired.\n", __func__); + + if (changed) { + hdspe->last_status_change_jiffies = 0; + hdspe->status_polling = 0; /* disable - user must re-enable */ + HDSPE_CTL_NOTIFY(status_polling); + } else if (jiffies > hdspe->last_status_change_jiffies + 2*HZ) { + dev_dbg(hdspe->card->dev, + "%s: polling timeout expired: jiffies=%lu, last_status_change_jiffied=%lu, delta=%ld, 2*HZ=%d.\n", __func__, + jiffies, hdspe->last_status_change_jiffies, + jiffies - hdspe->last_status_change_jiffies, 2*HZ); + hdspe->last_status_change_jiffies = 0; hdspe->status_polling = 0; /* disable - user must re-enable */ HDSPE_CTL_NOTIFY(status_polling); } - - if (changed) - hdspe->last_status_change_jiffies = jiffies; } @@ -809,7 +812,7 @@ int snd_hdspe_create_controls(struct snd_card *card, HDSPE_ADD_RV_CONTROL_ID(CARD, "Running", running); HDSPE_ADD_RV_CONTROL_ID(CARD, "Buffer Size", buffer_size); - HDSPE_ADD_RW_CONTROL_ID(CARD, "Status Polling", status_polling); + HDSPE_ADD_RWV_CONTROL_ID(CARD, "Status Polling", status_polling); HDSPE_ADD_RV_CONTROL_ID(HWDEP, "Raw Sample Rate", raw_sample_rate); HDSPE_ADD_RW_CONTROL_ID(HWDEP, "DDS", dds); HDSPE_ADD_RW_CONTROL_ID(CARD, "Internal Frequency", internal_freq); diff --git a/sound/pci/hdsp/hdspe/hdspe_control.h b/sound/pci/hdsp/hdspe/hdspe_control.h index a8afb04..77b101a 100644 --- a/sound/pci/hdsp/hdspe/hdspe_control.h +++ b/sound/pci/hdsp/hdspe/hdspe_control.h @@ -3,7 +3,7 @@ * @file hdspe-control.h * @brief RME HDSPe sound card driver status and control interface helpers. * - * 20210728,0907,08,09,10 - Philippe.Bekaert@uhasselt.be + * 20210728,0907,08,09,10,20220330 - Philippe.Bekaert@uhasselt.be * * Based on earlier work of the other MODULE_AUTHORs, * information kindly made available by RME (www.rme-audio.com), @@ -72,6 +72,23 @@ .put = snd_hdspe_put_##prop \ } +/** + * HDSPE_RWV_KCTL - generate a snd_kcontrol_new struct for a read-write + * volatile property. + * @xface: MIXER, CARD, etc... + * @xname: display name for the property. + * @prop: source code name for the property. + */ +#define HDSPE_RWV_KCTL(xface, xname, prop) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_##xface, \ + .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspe_info_##prop, \ + .get = snd_hdspe_get_##prop, \ + .put = snd_hdspe_put_##prop \ +} + /** * HDSPE_WO_KCTL - generate a snd_kcontrol_new struct for a write-only property. * @xface: MIXER, CARD, etc... @@ -444,5 +461,8 @@ static int snd_hdspe_put_##prop(struct snd_kcontrol *kcontrol, \ #define HDSPE_ADD_RW_BOOL_CONTROL_ID(iface, name, prop) \ HDSPE_ADD_CONTROL_ID(HDSPE_RW_BOOL_KCTL(iface, name, prop), prop) +#define HDSPE_ADD_RWV_CONTROL_ID(iface, name, prop) \ + HDSPE_ADD_CONTROL_ID(HDSPE_RWV_KCTL(iface, name, prop), prop) + #endif /* _HDSPE_CONTROL_H_ */ diff --git a/sound/pci/hdsp/hdspe/hdspe_core.c b/sound/pci/hdsp/hdspe/hdspe_core.c index e86e87b..4ab9545 100644 --- a/sound/pci/hdsp/hdspe/hdspe_core.c +++ b/sound/pci/hdsp/hdspe/hdspe_core.c @@ -432,6 +432,8 @@ static int snd_hdspe_create(struct hdspe *hdspe) pci_set_master(hdspe->pci); + /* TODO: mac driver sets PCI latency timer to 255 ??? */ + err = pci_request_regions(pci, "hdspe"); if (err < 0) return err; diff --git a/sound/pci/hdsp/hdspe/hdspe_core.h b/sound/pci/hdsp/hdspe/hdspe_core.h index e2f4d32..8bdd8a2 100644 --- a/sound/pci/hdsp/hdspe/hdspe_core.h +++ b/sound/pci/hdsp/hdspe/hdspe_core.h @@ -788,6 +788,7 @@ struct hdspe_tco { enum hdspe_pull pull; enum hdspe_wck_conversion wck_conversion; enum hdspe_bool term; + enum hdspe_speed wck_out_speed; /* LTC out control */ u32 ltc_out; /* requested start LTC for output */ @@ -807,7 +808,7 @@ struct hdspe_tco { /* for measuring the actual LTC In fps and pull factor */ #define LTC_CACHE_SIZE 60 - u64 prev_ltc_time; /* nanosecond timestamp of previous LTC irq */ + u64 prev_ltc_time; /* nanosecond timestamp of previous MTC irq */ u64 ltc_duration_sum; /* sum of observed LTC frame durations */ u32 ltc_duration[LTC_CACHE_SIZE]; /* observed LTC frame durations */ u32 ltc_count; /* number of received LTC frames */ @@ -818,6 +819,8 @@ struct hdspe_tco { #ifdef DEBUG_MTC u32 mtc; /* current MIDI time code */ #endif /*DEBUG_MTC*/ + + u8 fw_version; /* TCO firmware version */ }; /** @@ -905,6 +908,8 @@ struct hdspe_ctl_ids { struct snd_ctl_elem_id* tco_lock; struct snd_ctl_elem_id* ltc_run; struct snd_ctl_elem_id* ltc_jam_sync; + struct snd_ctl_elem_id* video_in_fps; + /* struct snd_ctl_elem_id* wck_out_rate; */ }; struct hdspe { diff --git a/sound/pci/hdsp/hdspe/hdspe_midi.c b/sound/pci/hdsp/hdspe/hdspe_midi.c index 1d9b8a7..f63b124 100644 --- a/sound/pci/hdsp/hdspe/hdspe_midi.c +++ b/sound/pci/hdsp/hdspe/hdspe_midi.c @@ -309,13 +309,13 @@ int snd_hdspe_create_midi(struct snd_card* card, err = snd_rawmidi_new(card, buf, id, 1, 1, &m->rmidi); if (err < 0) return err; - - snprintf(m->rmidi->name, sizeof(m->rmidi->name), - rw ? "%s MIDI %d" : "%s MTC %d", - card->id, id+1); + m->rmidi->private_data = &hdspe->midi[id]; if (rw) { // read-write port + snprintf(m->rmidi->name, sizeof(m->rmidi->name), + "%s MIDI %d", card->id, id+1); + snd_rawmidi_set_ops(m->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_hdspe_midi_output); @@ -328,6 +328,9 @@ int snd_hdspe_create_midi(struct snd_card* card, SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; } else { // read-only port (MTC from TCO module) + snprintf(m->rmidi->name, sizeof(m->rmidi->name), + "%s MTC", card->id); + snd_rawmidi_set_ops(m->rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_hdspe_midi_input); diff --git a/sound/pci/hdsp/hdspe/hdspe_tco.c b/sound/pci/hdsp/hdspe/hdspe_tco.c index 5a8b71e..8d1f98a 100644 --- a/sound/pci/hdsp/hdspe/hdspe_tco.c +++ b/sound/pci/hdsp/hdspe/hdspe_tco.c @@ -3,7 +3,8 @@ * hdspe-tco.c * @brief RME HDSPe Time Code Option driver status and control interface. * - * 20210728,0812,0902,24,28,1008,13,27 - Philippe.Bekaert@uhasselt.be + * 20210728,0812,0902,24,28,1008,13,27,20220325,29,30 + * - Philippe.Bekaert@uhasselt.be * * Based on earlier work of the other MODULE_AUTHORS, * information kindly made available by RME (www.rme-audio.com), @@ -23,7 +24,144 @@ #endif /*DEBUG_LTC*/ /** - * TCO register definitions + * TCO register definitions: + * + * TCO0 : contains time code. Status (at HDSPE_RD_TCO byte offset): report + * current time code. Control (at HDSPE_WR_TCO offset): time code to set next. + * + * POS MASK STATUS CONTROL (same as STATUS) + * + * 00 f frames units + * 04 30 frames tens + * 06 40 unused + * 07 80 sync (1) + * 08 f00 seconds units + * 12 7000 seconds tens + * 15 8000 sync (1) + * 16 f0000 minutes unts + * 20 700000 minutes tens + * 23 800000 sync (1) + * 24 f000000 hour units + * 28 30000000 hour tens + * 30 40000000 unused + * 31 80000000 sync (1) + * + * (1) the sync bits have no significance for what concerns the driver. + * + * TCO1 : status at byte offset HDSPE_RD_TCO+4, control at HDSPE_WR_TCO+4 + * + * POS MASK STATUS CONTROL + * + * 00 1 TCO lock internal WCK out defeat + * 01 2 WCK input range WCK output range 0=SS, 1=DS, 2=QS + * 02 4 " " + * 03 8 LTC input valid + * 04 10 WCK input valid + * 05 20 video in format NTSC + * 06 40 video in format PAL + * 07 80 sync sync + * 08 100 set TC upon rising edge + * 09 200 LTC rx drop frame LTC tx drop frame 0 = full frame, 1 = DF + * 10 400 LTC rx format LSB LTC tx format LSB 0=24, 1=25, 2=29.97, 3=30 + * 11 800 LTC rx format MSB LTC tx format MSB rx only 24/25/30 + * 12 1000 Q-frame nr LSB + * 13 2000 Q-frame nr MSB + * 14 4000 new quarter frame + * 15 8000 sync sync + * 16 10000 sample position LSB sample position LSB + * 17 20000 " " + * 18 40000 " " + * 19 80000 " " + * 20 100000 " " + * 21 200000 " " + * 22 400000 " " + * 23 800000 sync sync + * 24 1000000 sample position MSB sample position MSB + * 25 2000000 " " + * 26 4000000 " " + * 27 8000000 " " + * 28 10000000 " " + * 29 20000000 " " + * 30 40000000 " " + * 31 80000000 sync sync + * + * TCO2 : status at byte offset HDSPE_RD_TCO+8, control at HDSPE_WR_TCO+8 + * + * POS MASK STATUS CONTROL + * + * 00 1 FS period counter LSB WCK period counted at + * 01 2 " 25 MHz (10 bit), + * 02 4 " 16-sample sliding + * 03 8 " sum. + * 04 10 " + * 05 20 " + * 06 40 " + * 07 80 sync sync + * 08 100 FS period counter MSB + * 09 200 " + * 10 400 " + * 11 800 " + * 12 1000 " + * 13 2000 " + * 14 4000 " + * 15 8000 sync sync + * 16 10000 TC run 0=pause, 1=run + * 17 20000 WCK I/O rate 0=1:1, 1=44.1->48 + * 18 40000 " 2=48->44.1 + * 19 80000 output drop frames 0..2, 3=continuous + * 20 100000 " + * 21 200000 jam sync not implemented + * 22 400000 flywheel not implemented + * 23 800000 sync sync + * 24 1000000 0.1 / 4 0=0.1%, 1=4% + * 25 2000000 pull-down 0=off, 1=on + * 26 4000000 pull-up 0=off, 1=on + * 27 8000000 video in fps (1) sample rate 0=44.1KHz, 1=48KHz + * 28 10000000 " 75 Ohm termination 0=off, 1=on + * 29 20000000 " source select 0=WCK, 1=video, 2=LTC + * 30 40000000 " " + * 31 80000000 sync sync + * + * (1) firmware version 11 or later. 0=no lock, 1=23.98, 2=24, 3=25, 4=29.97 + * 5=30, 6=47.95, 7=48, 8=50, 9=59.94, 10=60 + * + * TCO3 : status at byte offset HDSPE_RD_TCO+12, control at HDSPE_WR_TCO+12 + * + * POS MASK STATUS CONTROL + * + * 00 1 sync sync + * 01 2 sync sync + * 02 4 sync sync + * 03 8 sync sync + * 04 10 sync sync + * 05 20 sync sync + * 06 40 sync sync + * 07 80 sync sync + * 08 100 sync sync + * 09 200 sync sync + * 10 400 sync sync + * 11 800 sync sync + * 12 1000 sync sync + * 13 2000 sync sync + * 14 4000 sync sync + * 15 8000 sync sync + * 16 10000 + * 17 20000 + * 18 40000 + * 19 80000 + * 20 100000 + * 21 200000 + * 22 400000 TC number encoding not implemented + * 23 800000 sync sync + * 24 1000000 version LSB + * 25 2000000 " + * 26 4000000 " + * 27 8000000 " + * 28 10000000 " + * 29 20000000 " + * 30 40000000 version MSB + * 31 80000000 sync sync + * */ #define HDSPE_TCO1_TCO_lock 0x00000001 @@ -39,7 +177,7 @@ #define HDSPE_TCO1_LTC_Format_LSB 0x00000400 #define HDSPE_TCO1_LTC_Format_MSB 0x00000800 -#define HDSPE_TCO1_STATUS_MASK 0x00000cff +#define HDSPE_TCO1_STATUS_MASK 0x00000c7f #define HDSPE_TCO2_TC_run 0x00010000 #define HDSPE_TCO2_WCK_IO_ratio_LSB 0x00020000 @@ -67,16 +205,16 @@ static const char* const tco1_bitNames[32] = { "WCK_Input_valid", "Video_Input_Format_NTSC", "Video_Input_Format_PAL", - "?7", + "sync", "set_TC", "set_drop_frame_flag", "LTC_Format_LSB", "LTC_Format_MSB", - "?12", - "?13", - "?14", - "?15", + "Q-frame nr LSB", + "Q-frame nr MSB", + "new Q-frame", + "sync", "off0", "off1", @@ -85,7 +223,7 @@ static const char* const tco1_bitNames[32] = { "off4", "off5", "off6", - "?23", + "sync", "off7", "off8", @@ -94,7 +232,7 @@ static const char* const tco1_bitNames[32] = { "off11", "off12", "off13", - "?31" + "sync" }; #ifdef NEVER @@ -106,7 +244,7 @@ static const char* const tco2_bitNames[32] = { "?04", "?05", "?06", - "?07", + "sync", "?08", "?09", @@ -115,7 +253,7 @@ static const char* const tco2_bitNames[32] = { "?12", "?13", "?14", - "?15", + "sync", "TC_run", "WCK_IO_ratio_LSB", @@ -124,7 +262,7 @@ static const char* const tco2_bitNames[32] = { "set_num_drop_frames_MSB", "set_jam_sync", "set_flywheel", - "?23", + "sync", "set_01_4", "set_pull_down", @@ -182,6 +320,7 @@ u32 hdspe_read_tco(struct hdspe* hdspe, unsigned n) static inline __attribute__((always_inline)) void hdspe_write_tco(struct hdspe* hdspe, unsigned n, u32 value) { + value &= 0x7f7f7f7f; hdspe_write(hdspe, HDSPE_WR_TCO+n*4, cpu_to_le32(value)); } @@ -207,6 +346,14 @@ static void hdspe_tco_read_status1(struct hdspe* hdspe, s->ltc_in_offset = ((tco1 >> 16) & 0x7F) | ((tco1 >> 17) & 0x3F80); } +static void hdspe_tco_read_status2(struct hdspe* hdspe, + struct hdspe_tco_status* s) +{ + u32 tco2 = hdspe_read_tco(hdspe, 2); + s->fs_period_counter = (tco2 & 0x7F) | ((tco2 & 0x7F00) >> 1); + s->video_in_fps = (tco2 >> 27) & 0x0F; +} + static void hdspe_tco_copy_control(struct hdspe* hdspe, struct hdspe_tco_status* s) { @@ -223,15 +370,19 @@ static void hdspe_tco_copy_control(struct hdspe* hdspe, s->term = hdspe->tco->term; s->ltc_run = hdspe->tco->ltc_run; - s->ltc_flywheel = hdspe->tco->ltc_flywheel; + s->ltc_flywheel = hdspe->tco->ltc_flywheel; + + s->wck_out_speed = hdspe->tco->wck_out_speed; } void hdspe_tco_read_status(struct hdspe* hdspe, struct hdspe_tco_status* s) { spin_lock(&hdspe->tco->lock); s->version = HDSPE_VERSION; + s->fw_version = hdspe->tco->fw_version; s->ltc_in = hdspe_read_tco(hdspe, 0); hdspe_tco_read_status1(hdspe, s); + hdspe_tco_read_status2(hdspe, s); hdspe_tco_copy_control(hdspe, s); spin_unlock(&hdspe->tco->lock); } @@ -258,6 +409,8 @@ static void hdspe_tco_write_settings(struct hdspe* hdspe) reg[0] = reg[1] = reg[2] = reg[3] = 0; + reg[1] |= FIELD_PREP(HDSPE_TCO1_WCK_Input_Range_MSB| + HDSPE_TCO1_WCK_Input_Range_LSB, c->wck_out_speed); reg[1] |= FIELD_PREP(HDSPE_TCO1_LTC_Format_MSB| HDSPE_TCO1_LTC_Format_LSB, c->ltc_fps); reg[1] |= FIELD_PREP(HDSPE_TCO1_set_drop_frame_flag, c->ltc_drop); @@ -270,6 +423,7 @@ static void hdspe_tco_write_settings(struct hdspe* hdspe) c->sample_rate == HDSPE_TCO_SAMPLE_RATE_48 || (c->sample_rate == HDSPE_TCO_SAMPLE_RATE_FROM_APP && sys_48KHz)); + reg[2] |= FIELD_PREP(HDSPE_TCO2_set_freq_from_app, c->sample_rate == HDSPE_TCO_SAMPLE_RATE_FROM_APP); reg[2] |= FIELD_PREP(HDSPE_TCO2_set_term_75R, c->term); @@ -287,9 +441,9 @@ static void hdspe_tco_write_settings(struct hdspe* hdspe) void hdspe_tco_set_app_sample_rate(struct hdspe* hdspe) { - /* Set TCO2_set_freq bit when internal frequency - * of the sound card is changed to 48KHz or multiple thereof, and - * TCO sample rate is "From App". */ + /* Set/clear TCO2_set_freq bit when internal frequency + * of the sound card is changed to something not corresponding + * with TCO card frequency, and TCO sample rate is "From App". */ struct hdspe_tco* c = hdspe->tco; bool tco_48KHz, sys_48KHz; if (!c) @@ -323,8 +477,10 @@ static void hdspe_tco_set_timecode(struct hdspe* hdspe, u32 timecode, u16 offset) { struct hdspe_tco* c = hdspe->tco; + /* offset is stored as two groups of 7 bits */ + uint32_t offset2 = ((offset & 0x3f80)<<1) | (offset & 0x7f); hdspe_write_tco(hdspe, 0, timecode); - hdspe_write_tco(hdspe, 1, (offset << 16) | HDSPE_TCO1_set_TC | + hdspe_write_tco(hdspe, 1, (offset2 << 16) | HDSPE_TCO1_set_TC | (c->reg[1] & 0xffff)); c->ltc_set = true; @@ -358,7 +514,8 @@ struct hdspe_ltc { static const u32 hdspe_fps_tab[4] = { 24, 25, 30, 30 }; static const u32 hdspe_scale_tab[4] = {1000, 1000, 999, 1000 }; -/* Offsets needed when starting time code, experimentally determined and verified. */ +/* Offsets needed when starting time code, experimentally determined and + * verified. */ static u32 hdspe_ltc_offset(u32 fps, enum hdspe_freq f) { u32 offset = 0; @@ -411,7 +568,7 @@ static void hdspe_tco_start_timecode(struct hdspe* hdspe) int n; /* compensate this many frames w.r.t. pickup at next period */ s32 offset; /* nr of samples to delay LTC start at next period */ u32 sr = hdspe_tco_get_sample_rate(hdspe); /* sample rate */ - u32 speedfactor = hdspe_speed_factor(hdspe); + u32 speedfactor = hdspe_speed_factor(hdspe); /* 1, 2 or 4 */ struct hdspe_ltc ltc; ltc.tc = c->ltc_out; @@ -420,9 +577,9 @@ static void hdspe_tco_start_timecode(struct hdspe* hdspe) ltc.scale = hdspe_scale_tab[c->ltc_fps]; ltc.df = c->ltc_drop; - ltc.fc /= speedfactor; /* need single speed offset */ - cfc /= speedfactor; - ps /= speedfactor; + ltc.fc /= speedfactor; /* need single speed offset, */ + cfc /= speedfactor; /* frame count */ + ps /= speedfactor; /* and period size */ fs = sr * 1000 / (ltc.fps * ltc.scale); @@ -673,8 +830,10 @@ void snd_hdspe_proc_read_tco(struct snd_info_entry *entry, struct hdspe *hdspe = entry->private_data; struct hdspe_tco *c = hdspe->tco; struct hdspe_tco_status s; - u32 tco1 = hdspe_read_tco(hdspe, 1); u32 ltc = hdspe_read_tco(hdspe, 0); + u32 tco1 = hdspe_read_tco(hdspe, 1); + u32 tco2 = hdspe_read_tco(hdspe, 2); + u32 tco3 = hdspe_read_tco(hdspe, 3); if (!c) { snd_BUG(); @@ -736,6 +895,13 @@ void snd_hdspe_proc_read_tco(struct snd_info_entry *entry, c->ltc_flywheel, HDSPE_BOOL_NAME(c->ltc_flywheel)); snd_iprintf(buffer, "LTC Set : %d %s\n", c->ltc_set, HDSPE_BOOL_NAME(c->ltc_set)); + + snd_iprintf(buffer, "TCO FW version : %d\n", + (tco3 >> 24) & 0x7f); + snd_iprintf(buffer, "TCO WCK period : %d/%d\n", + ((tco2 & 0x7f00)>>1) | (tco2&0x7f), 25000000); + snd_iprintf(buffer, "Video Input FPS : %d %s\n", + (tco2 >> 27) & 0x0f, ""); } //////////////////////////////////////////////////////////////////////// @@ -752,6 +918,18 @@ static int hdspe_tco_get_status(struct hdspe* hdspe, return val; } +static int hdspe_tco_get_status2(struct hdspe* hdspe, + int (*getter)(struct hdspe_tco_status*), + const char* propname) +{ + struct hdspe_tco_status status; + int val; + hdspe_tco_read_status2(hdspe, &status); + val = getter(&status); + dev_dbg(hdspe->card->dev, "%s(%s) = %d.\n", __func__, propname, val); + return val; +} + static int hdspe_tco_put_control(struct hdspe* hdspe, int val, int maxrange, int (*putter)(struct hdspe_tco*, int val), @@ -797,6 +975,30 @@ HDSPE_TCO_STATUS_GET_WITHOUT_GETTER(prop, item, field) HDSPE_TCO_STATUS_GET(prop, integer.value, field) +#define HDSPE_TCO_STATUS2_GET_WITHOUT_GETTER(prop, item, field) \ +static int snd_hdspe_get_##prop(struct snd_kcontrol *kcontrol, \ + struct snd_ctl_elem_value *ucontrol) \ +{ \ + struct hdspe* hdspe = snd_kcontrol_chip(kcontrol); \ + ucontrol->value.item[0] = hdspe_tco_get_status2( \ + hdspe, hdspe_tco_get_status_##field, #prop); \ + return 0; \ +} + +#define HDSPE_TCO_STATUS2_GET(prop, item, field) \ +static int hdspe_tco_get_status_##field(struct hdspe_tco_status* s) \ +{ \ + return s->field; \ +} \ +HDSPE_TCO_STATUS2_GET_WITHOUT_GETTER(prop, item, field) + +#define HDSPE_TCO_STATUS2_ENUM_METHODS(prop, field) \ + HDSPE_TCO_STATUS2_GET(prop, enumerated.item, field) + +#define HDSPE_TCO_STATUS2_INT_METHODS(prop, field) \ + HDSPE_TCO_STATUS2_GET(prop, integer.value, field) + + #define HDSPE_TCO_CONTROL_GET_WITHOUT_GETTER(prop, item, field) \ static int snd_hdspe_get_##prop(struct snd_kcontrol *kcontrol, \ struct snd_ctl_elem_value *ucontrol) \ @@ -867,6 +1069,28 @@ static int snd_hdspe_info_video(struct snd_kcontrol *kcontrol, } HDSPE_TCO_STATUS_ENUM_METHODS(video, video) + +static int snd_hdspe_info_video_in_fps(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char *const texts[HDSPE_VIDEO_FPS_COUNT] = { + HDSPE_VIDEO_FPS_NAME(0), + HDSPE_VIDEO_FPS_NAME(1), + HDSPE_VIDEO_FPS_NAME(2), + HDSPE_VIDEO_FPS_NAME(3), + HDSPE_VIDEO_FPS_NAME(4), + HDSPE_VIDEO_FPS_NAME(5), + HDSPE_VIDEO_FPS_NAME(6), + HDSPE_VIDEO_FPS_NAME(7), + HDSPE_VIDEO_FPS_NAME(8), + HDSPE_VIDEO_FPS_NAME(9), + HDSPE_VIDEO_FPS_NAME(10) + }; + ENUMERATED_CTL_INFO(uinfo, texts); + return 0; +} + +HDSPE_TCO_STATUS2_ENUM_METHODS(video_in_fps, video_in_fps) HDSPE_TCO_STATUS_ENUM_METHODS(wck_valid, wck_valid) @@ -951,6 +1175,20 @@ static int snd_hdspe_info_wck_conversion(struct snd_kcontrol *kcontrol, HDSPE_TCO_CONTROL_ENUM_METHODS(wck_conversion, wck_conversion, HDSPE_WCK_CONVERSION_COUNT) +static int snd_hdspe_info_wck_out_speed(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char *const texts[HDSPE_SPEED_COUNT] = { + HDSPE_SPEED_NAME(0), + HDSPE_SPEED_NAME(1), + HDSPE_SPEED_NAME(2) + }; + ENUMERATED_CTL_INFO(uinfo, texts); + return 0; +} + +HDSPE_TCO_CONTROL_ENUM_METHODS(wck_out_speed, wck_out_speed, HDSPE_SPEED_COUNT); + static int snd_hdspe_info_frame_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1089,8 +1327,49 @@ static int snd_hdspe_put_ltc_out(struct snd_kcontrol *kcontrol, return 0; /* do not notify */ } +#ifdef NEVER +static int snd_hdspe_info_wck_out_rate(struct snd_kcontrol* kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + return 0; +} + +static int snd_hdspe_get_wck_out_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspe *hdspe = snd_kcontrol_chip(kcontrol); + struct hdspe_tco_status s; + hdspe_tco_read_status2(hdspe, &s); + ucontrol->value.integer.value[0] = 25000000 * 16; /* 25 MHz * 16*/ + ucontrol->value.integer.value[1] = s.fs_period_counter; + dev_dbg(hdspe->card->dev, "%s = %d %d\n", __func__, + (int)ucontrol->value.integer.value[0], + (int)ucontrol->value.integer.value[1]); + return 0; +} +#endif /*NEVER*/ + +static int snd_hdspe_info_fw_version(struct snd_kcontrol* kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + return 0; +} + +static int snd_hdspe_get_fw_version(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspe *hdspe = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = hdspe->tco->fw_version; + return 0; +} + /* Control elements for the optional TCO module */ static const struct snd_kcontrol_new snd_hdspe_controls_tco[] = { + HDSPE_RO_KCTL(CARD, "TCO Firmware", fw_version), HDSPE_RW_KCTL(CARD, "LTC Sample Rate", sample_rate), HDSPE_RW_KCTL(CARD, "TCO Pull", pull), HDSPE_RW_KCTL(CARD, "TCO WordClk Conversion", wck_conversion), @@ -1098,7 +1377,8 @@ static const struct snd_kcontrol_new snd_hdspe_controls_tco[] = { HDSPE_RW_KCTL(CARD, "TCO Sync Source", sync_source), HDSPE_RW_BOOL_KCTL(CARD, "TCO WordClk Term", word_term), HDSPE_WO_KCTL(CARD, "LTC Out", ltc_out), - HDSPE_RV_KCTL(CARD, "LTC Time", ltc_time) + HDSPE_RV_KCTL(CARD, "LTC Time", ltc_time), + HDSPE_RW_KCTL(CARD, "TCO WordClk Out Speed", wck_out_speed) }; #define CHECK_STATUS_CHANGE(prop) \ @@ -1120,6 +1400,7 @@ bool hdspe_tco_notify_status_change(struct hdspe* hdspe) CHECK_STATUS_CHANGE(ltc_in_fps); CHECK_STATUS_CHANGE(ltc_in_drop); CHECK_STATUS_CHANGE(video); + CHECK_STATUS_CHANGE(video_in_fps); CHECK_STATUS_CHANGE(wck_valid); CHECK_STATUS_CHANGE(wck_speed); CHECK_STATUS_CHANGE(tco_lock); @@ -1140,9 +1421,13 @@ int hdspe_create_tco_controls(struct hdspe* hdspe) HDSPE_ADD_RV_BOOL_CONTROL_ID(CARD, "LTC In Drop Frame", ltc_in_drop); HDSPE_ADD_RV_CONTROL_ID(CARD, "LTC In Pull Factor", ltc_in_pullfac); HDSPE_ADD_RV_CONTROL_ID(CARD, "TCO Video Format", video); + HDSPE_ADD_RV_CONTROL_ID(CARD, "TCO Video Frame Rate", video_in_fps); HDSPE_ADD_RV_BOOL_CONTROL_ID(CARD, "TCO WordClk Valid", wck_valid); HDSPE_ADD_RV_CONTROL_ID(CARD, "TCO WordClk Speed", wck_speed); HDSPE_ADD_RV_BOOL_CONTROL_ID(CARD, "TCO Lock", tco_lock); +#ifdef NEVER + HDSPE_ADD_RV_CONTROL_ID(CARD, "TCO WordClk Out Rate", wck_out_rate); +#endif /*NEVER*/ HDSPE_ADD_RW_BOOL_CONTROL_ID(CARD, "LTC Run", ltc_run); @@ -1184,11 +1469,14 @@ int hdspe_init_tco(struct hdspe* hdspe) spin_lock_init(&hdspe->tco->lock); hdspe->midiPorts++; - dev_info(hdspe->card->dev, "TCO module found\n"); /* hdspe->tco->ltc_out = 0xffffffff; would not set LTC output */ hdspe_tco_write_settings(hdspe); + hdspe->tco->fw_version = (hdspe_read_tco(hdspe, 3) >> 24) & 0x7f; + dev_info(hdspe->card->dev, "TCO module found. Firmware version %d.\n", + hdspe->tco->fw_version); + #ifdef DEBUG_LTC timer_setup(&hdspe->tco_timer, hdspe_tco_timer, 0); mod_timer(&hdspe->tco_timer, jiffies+HZ/LTC_TIMER_FREQ); diff --git a/sound/pci/hdsp/hdspe/snd-hdspe.mod.c b/sound/pci/hdsp/hdspe/snd-hdspe.mod.c new file mode 100644 index 0000000..9d279af --- /dev/null +++ b/sound/pci/hdsp/hdspe/snd-hdspe.mod.c @@ -0,0 +1,131 @@ +#include +#define INCLUDE_VERMAGIC +#include +#include +#include +#include + +BUILD_SALT; +BUILD_LTO_INFO; + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_INFO(name, KBUILD_MODNAME); + +__visible struct module __this_module +__section(".gnu.linkonce.this_module") = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +#ifdef CONFIG_RETPOLINE +MODULE_INFO(retpoline, "Y"); +#endif + +static const struct modversion_info ____versions[] +__used __section("__versions") = { + { 0x5c4cd626, "module_layout" }, + { 0x2d3385d3, "system_wq" }, + { 0xc5bca62a, "kmalloc_caches" }, + { 0xc4f0da12, "ktime_get_with_offset" }, + { 0xb5b54b34, "_raw_spin_unlock" }, + { 0xe8b88f8c, "param_ops_int" }, + { 0x2b68bd2f, "del_timer" }, + { 0x754d539c, "strlen" }, + { 0xfd0e606e, "snd_pcm_period_elapsed" }, + { 0x263ed23b, "__x86_indirect_thunk_r12" }, + { 0xf2ee5fac, "dma_set_mask" }, + { 0xb48cc5df, "snd_pcm_hw_constraint_msbits" }, + { 0xfe903b2a, "pci_disable_device" }, + { 0xfff5afc, "time64_to_tm" }, + { 0x3e556aff, "seq_printf" }, + { 0x56470118, "__warn_printk" }, + { 0x3c12dfe, "cancel_work_sync" }, + { 0x39bf9301, "_snd_pcm_hw_param_setempty" }, + { 0x1f60b17e, "pci_release_regions" }, + { 0x8e59a22d, "param_ops_bool" }, + { 0xc6f46339, "init_timer_key" }, + { 0x816cfd6a, "snd_rawmidi_set_ops" }, + { 0xdd64e639, "strscpy" }, + { 0x775677fc, "dma_set_coherent_mask" }, + { 0x15ba50a6, "jiffies" }, + { 0x340f1fe2, "snd_rawmidi_new" }, + { 0x6b10bee1, "_copy_to_user" }, + { 0x999d613a, "param_ops_charp" }, + { 0x62d5dc27, "pci_set_master" }, + { 0xbe836042, "_dev_warn" }, + { 0xfb578fc5, "memset" }, + { 0xd35cce70, "_raw_spin_unlock_irqrestore" }, + { 0x54ce3499, "current_task" }, + { 0x55f7d5f8, "snd_pcm_hw_constraint_list" }, + { 0x9ec6ca96, "ktime_get_real_ts64" }, + { 0xde80cd09, "ioremap" }, + { 0x5c993fd7, "snd_pcm_set_ops" }, + { 0xab8a51b2, "snd_ctl_notify" }, + { 0xb91e4297, "snd_pcm_hw_constraint_pow2" }, + { 0x4b750f53, "_raw_spin_unlock_irq" }, + { 0x62985987, "pci_read_config_word" }, + { 0xaa0b33da, "snd_hwdep_new" }, + { 0xebbff79e, "snd_pcm_lib_free_pages" }, + { 0xc38c83b8, "mod_timer" }, + { 0xce8b1878, "__x86_indirect_thunk_r14" }, + { 0x92d5838e, "request_threaded_irq" }, + { 0x207529da, "snd_pcm_lib_ioctl" }, + { 0xdeb29e6e, "_dev_err" }, + { 0x94098ff8, "snd_interval_list" }, + { 0x5c86e297, "snd_card_set_id" }, + { 0x440d1aa9, "snd_ctl_boolean_mono_info" }, + { 0x5e3e45c3, "snd_pcm_lib_malloc_pages" }, + { 0x7cb72933, "snd_card_new" }, + { 0xcc6a729f, "snd_ctl_enum_info" }, + { 0x6f51809c, "_dev_info" }, + { 0xa6b2bd8, "snd_pcm_hw_rule_add" }, + { 0xb601be4c, "__x86_indirect_thunk_rdx" }, + { 0xa916b694, "strnlen" }, + { 0xc959d152, "__stack_chk_fail" }, + { 0xb8b9f817, "kmalloc_order_trace" }, + { 0x8427cc7b, "_raw_spin_lock_irq" }, + { 0x395a0453, "snd_ctl_new1" }, + { 0x2ea2c95c, "__x86_indirect_thunk_rax" }, + { 0xbdfb6dbb, "__fentry__" }, + { 0xcbd4898c, "fortify_panic" }, + { 0xeb0c30f0, "pci_unregister_driver" }, + { 0x90b9d0d7, "snd_pcm_set_sync" }, + { 0xea34ab69, "kmem_cache_alloc_trace" }, + { 0xba8fbd64, "_raw_spin_lock" }, + { 0xb76580fb, "__dynamic_dev_dbg" }, + { 0x34db050b, "_raw_spin_lock_irqsave" }, + { 0xcd91b127, "system_highpri_wq" }, + { 0x2414890c, "snd_card_rw_proc_new" }, + { 0x4cda566, "snd_interval_refine" }, + { 0x37a0cba, "kfree" }, + { 0x69acdf38, "memcpy" }, + { 0x171e64f9, "pci_request_regions" }, + { 0xe46e126b, "param_array_ops" }, + { 0x5eaece58, "snd_pcm_hw_constraint_minmax" }, + { 0xedc03953, "iounmap" }, + { 0xe223133a, "__pci_register_driver" }, + { 0x401c9a08, "snd_pcm_lib_preallocate_pages_for_all" }, + { 0x16078625, "snd_card_free" }, + { 0x67058b4e, "snd_card_register" }, + { 0xc5b6f236, "queue_work_on" }, + { 0x656e4a6e, "snprintf" }, + { 0xfd25d016, "snd_pcm_new" }, + { 0x7081a567, "snd_ctl_add" }, + { 0x7c980197, "snd_rawmidi_transmit" }, + { 0xa818538b, "pci_enable_device" }, + { 0x13c49cc2, "_copy_from_user" }, + { 0x18177e12, "snd_rawmidi_transmit_empty" }, + { 0xd91e8c4, "snd_rawmidi_receive" }, + { 0xc1514a3b, "free_irq" }, +}; + +MODULE_INFO(depends, "snd-pcm,snd-rawmidi,snd,snd-hwdep"); + +MODULE_ALIAS("pci:v000010EEd00003FC6sv*sd*bc*sc*i*"); +MODULE_ALIAS("pci:v00001D18d00003FC6sv*sd*bc*sc*i*"); + +MODULE_INFO(srcversion, "A99F46C6665D4B693DD2455");