diff options
Diffstat (limited to 'sound')
59 files changed, 1573 insertions, 399 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ec9e7866177f..446c00bd908b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1015,6 +1015,60 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, EXPORT_SYMBOL(snd_interval_list); +/** + * snd_interval_ranges - refine the interval value from the list of ranges + * @i: the interval value to refine + * @count: the number of elements in the list of ranges + * @ranges: the ranges list + * @mask: the bit-mask to evaluate + * + * Refines the interval value from the list of ranges. + * When mask is non-zero, only the elements corresponding to bit 1 are + * evaluated. + * + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. + */ +int snd_interval_ranges(struct snd_interval *i, unsigned int count, + const struct snd_interval *ranges, unsigned int mask) +{ + unsigned int k; + struct snd_interval range_union; + struct snd_interval range; + + if (!count) { + snd_interval_none(i); + return -EINVAL; + } + snd_interval_any(&range_union); + range_union.min = UINT_MAX; + range_union.max = 0; + for (k = 0; k < count; k++) { + if (mask && !(mask & (1 << k))) + continue; + snd_interval_copy(&range, &ranges[k]); + if (snd_interval_refine(&range, i) < 0) + continue; + if (snd_interval_empty(&range)) + continue; + + if (range.min < range_union.min) { + range_union.min = range.min; + range_union.openmin = 1; + } + if (range.min == range_union.min && !range.openmin) + range_union.openmin = 0; + if (range.max > range_union.max) { + range_union.max = range.max; + range_union.openmax = 1; + } + if (range.max == range_union.max && !range.openmax) + range_union.openmax = 0; + } + return snd_interval_refine(i, &range_union); +} +EXPORT_SYMBOL(snd_interval_ranges); + static int snd_interval_step(struct snd_interval *i, unsigned int step) { unsigned int n; @@ -1221,6 +1275,37 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, EXPORT_SYMBOL(snd_pcm_hw_constraint_list); +static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hw_constraint_ranges *r = rule->private; + return snd_interval_ranges(hw_param_interval(params, rule->var), + r->count, r->ranges, r->mask); +} + + +/** + * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter + * @runtime: PCM runtime instance + * @cond: condition bits + * @var: hw_params variable to apply the list of range constraints + * @r: ranges + * + * Apply the list of range constraints to an interval parameter. + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + const struct snd_pcm_hw_constraint_ranges *r) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_ranges, (void *)r, + var, -1); +} +EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges); + static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index ec667f158f19..5d905d90d504 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -82,36 +82,6 @@ struct snd_seq_dummy_port { static int my_client = -1; /* - * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events - * to subscribers. - * Note: this callback is called only after all subscribers are removed. - */ -static int -dummy_unuse(void *private_data, struct snd_seq_port_subscribe *info) -{ - struct snd_seq_dummy_port *p; - int i; - struct snd_seq_event ev; - - p = private_data; - memset(&ev, 0, sizeof(ev)); - if (p->duplex) - ev.source.port = p->connect; - else - ev.source.port = p->port; - ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; - ev.type = SNDRV_SEQ_EVENT_CONTROLLER; - for (i = 0; i < 16; i++) { - ev.data.control.channel = i; - ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF; - snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0); - ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS; - snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0); - } - return 0; -} - -/* * event input callback - just redirect events to subscribers */ static int @@ -175,7 +145,6 @@ create_port(int idx, int type) | SNDRV_SEQ_PORT_TYPE_PORT; memset(&pcb, 0, sizeof(pcb)); pcb.owner = THIS_MODULE; - pcb.unuse = dummy_unuse; pcb.event_input = dummy_input; pcb.private_free = dummy_free; pcb.private_data = rec; diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 3badc70124ab..0d580186ef1a 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -21,7 +21,19 @@ #define CYCLES_PER_SECOND 8000 #define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) -#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */ +/* + * Nominally 3125 bytes/second, but the MIDI port's clock might be + * 1% too slow, and the bus clock 100 ppm too fast. + */ +#define MIDI_BYTES_PER_SECOND 3093 + +/* + * Several devices look only at the first eight data blocks. + * In any case, this is more than enough for the MIDI data rate. + */ +#define MAX_MIDI_RX_BLOCKS 8 + +#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */ /* isochronous header parameters */ #define ISO_DATA_LENGTH_SHIFT 16 @@ -78,8 +90,6 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->callbacked = false; s->sync_slave = NULL; - s->rx_blocks_for_midi = UINT_MAX; - return 0; } EXPORT_SYMBOL(amdtp_stream_init); @@ -222,6 +232,14 @@ sfc_found: for (i = 0; i < pcm_channels; i++) s->pcm_positions[i] = i; s->midi_position = s->pcm_channels; + + /* + * We do not know the actual MIDI FIFO size of most devices. Just + * assume two bytes, i.e., one byte can be received over the bus while + * the previous one is transmitted over MIDI. + * (The value here is adjusted for midi_ratelimit_per_packet().) + */ + s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; } EXPORT_SYMBOL(amdtp_stream_set_parameters); @@ -463,6 +481,36 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s, } } +/* + * To avoid sending MIDI bytes at too high a rate, assume that the receiving + * device has a FIFO, and track how much it is filled. This values increases + * by one whenever we send one byte in a packet, but the FIFO empties at + * a constant rate independent of our packet rate. One packet has syt_interval + * samples, so the number of bytes that empty out of the FIFO, per packet(!), + * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing + * fractional values, the values in midi_fifo_used[] are measured in bytes + * multiplied by the sample rate. + */ +static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) +{ + int used; + + used = s->midi_fifo_used[port]; + if (used == 0) /* common shortcut */ + return true; + + used -= MIDI_BYTES_PER_SECOND * s->syt_interval; + used = max(used, 0); + s->midi_fifo_used[port] = used; + + return used < s->midi_fifo_limit; +} + +static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) +{ + s->midi_fifo_used[port] += amdtp_rate_table[s->sfc]; +} + static void amdtp_fill_midi(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { @@ -470,16 +518,21 @@ static void amdtp_fill_midi(struct amdtp_stream *s, u8 *b; for (f = 0; f < frames; f++) { - buffer[s->midi_position] = 0; b = (u8 *)&buffer[s->midi_position]; port = (s->data_block_counter + f) % 8; - if ((f >= s->rx_blocks_for_midi) || - (s->midi[port] == NULL) || - (snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0)) - b[0] = 0x80; - else + if (f < MAX_MIDI_RX_BLOCKS && + midi_ratelimit_per_packet(s, port) && + s->midi[port] != NULL && + snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) { + midi_rate_use_one_byte(s, port); b[0] = 0x81; + } else { + b[0] = 0x80; + b[1] = 0; + } + b[2] = 0; + b[3] = 0; buffer += s->data_block_quadlets; } diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index e6e8926275b0..8a03a91e728b 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -148,13 +148,12 @@ struct amdtp_stream { bool double_pcm_frames; struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + int midi_fifo_limit; + int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; /* quirk: fixed interval of dbc between previos/current packets. */ unsigned int tx_dbc_interval; - /* quirk: the first count of data blocks in an rx packet for MIDI */ - unsigned int rx_blocks_for_midi; - bool callbacked; wait_queue_head_t callback_wait; struct amdtp_stream *sync_slave; diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 1aab0a32870c..0ebcabfdc7ce 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -484,13 +484,6 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) amdtp_stream_destroy(&bebob->rx_stream); destroy_both_connections(bebob); } - /* - * The firmware for these devices ignore MIDI messages in more than - * first 8 data blocks of an received AMDTP packet. - */ - if (bebob->spec == &maudio_fw410_spec || - bebob->spec == &maudio_special_spec) - bebob->rx_stream.rx_blocks_for_midi = 8; end: return err; } diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index b985fc5ebdc6..4f440e163667 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -179,11 +179,6 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw) destroy_stream(efw, &efw->tx_stream); goto end; } - /* - * Fireworks ignores MIDI messages in more than first 8 data - * blocks of an received AMDTP packet. - */ - efw->rx_stream.rx_blocks_for_midi = 8; /* set IEC61883 compliant mode (actually not fully compliant...) */ err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883); diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c index 255dabc6fc33..2a85e4209f0b 100644 --- a/sound/firewire/fireworks/fireworks_transaction.c +++ b/sound/firewire/fireworks/fireworks_transaction.c @@ -124,7 +124,7 @@ copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode) spin_lock_irq(&efw->lock); t = (struct snd_efw_transaction *)data; - length = min_t(size_t, t->length * sizeof(t->length), length); + length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length); if (efw->push_ptr < efw->pull_ptr) capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr); diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 8276a743e22e..0cfc9c8c4b4e 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1922,10 +1922,18 @@ int azx_mixer_create(struct azx *chip) EXPORT_SYMBOL_GPL(azx_mixer_create); +static bool is_input_stream(struct azx *chip, unsigned char index) +{ + return (index >= chip->capture_index_offset && + index < chip->capture_index_offset + chip->capture_streams); +} + /* initialize SD streams */ int azx_init_stream(struct azx *chip) { int i; + int in_stream_tag = 0; + int out_stream_tag = 0; /* initialize each stream (aka device) * assign the starting bdl address to each stream (device) @@ -1938,9 +1946,21 @@ int azx_init_stream(struct azx *chip) azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ azx_dev->sd_int_sta_mask = 1 << i; - /* stream tag: must be non-zero and unique */ azx_dev->index = i; - azx_dev->stream_tag = i + 1; + + /* stream tag must be unique throughout + * the stream direction group, + * valid values 1...15 + * use separate stream tag if the flag + * AZX_DCAPS_SEPARATE_STREAM_TAG is used + */ + if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) + azx_dev->stream_tag = + is_input_stream(chip, i) ? + ++in_stream_tag : + ++out_stream_tag; + else + azx_dev->stream_tag = i + 1; } return 0; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 2bf0b568e3de..d426a0bd6a5f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -299,6 +299,9 @@ enum { AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ AZX_DCAPS_SNOOP_TYPE(SCH)) +#define AZX_DCAPS_INTEL_SKYLAKE \ + (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG) + /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB |\ @@ -2027,7 +2030,7 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c), .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h index aa484fdf4338..166e3e84b963 100644 --- a/sound/pci/hda/hda_priv.h +++ b/sound/pci/hda/hda_priv.h @@ -171,6 +171,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ #define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ +#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ enum { AZX_SNOOP_TYPE_NONE , diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 5f13d2d18079..b422e406a9cb 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3353,6 +3353,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, { .id = 0x10de0070, .name = "GPU 70 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0071, .name = "GPU 71 HDMI/DP", .patch = patch_nvhdmi }, +{ .id = 0x10de0072, .name = "GPU 72 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, { .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi }, { .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi }, @@ -3413,6 +3414,7 @@ MODULE_ALIAS("snd-hda-codec-id:10de0060"); MODULE_ALIAS("snd-hda-codec-id:10de0067"); MODULE_ALIAS("snd-hda-codec-id:10de0070"); MODULE_ALIAS("snd-hda-codec-id:10de0071"); +MODULE_ALIAS("snd-hda-codec-id:10de0072"); MODULE_ALIAS("snd-hda-codec-id:10de8001"); MODULE_ALIAS("snd-hda-codec-id:11069f80"); MODULE_ALIAS("snd-hda-codec-id:11069f81"); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 4f6413e01c13..605d14003d25 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -568,9 +568,9 @@ static void stac_store_hints(struct hda_codec *codec) spec->gpio_mask; } if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir)) - spec->gpio_mask &= spec->gpio_mask; - if (get_int_hint(codec, "gpio_data", &spec->gpio_data)) spec->gpio_dir &= spec->gpio_mask; + if (get_int_hint(codec, "gpio_data", &spec->gpio_data)) + spec->gpio_data &= spec->gpio_mask; if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask)) spec->eapd_mask &= spec->gpio_mask; if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute)) diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c index 7752860f7230..4c23381727a1 100644 --- a/sound/soc/adi/axi-i2s.c +++ b/sound/soc/adi/axi-i2s.c @@ -240,6 +240,8 @@ static int axi_i2s_probe(struct platform_device *pdev) if (ret) goto err_clk_disable; + return 0; + err_clk_disable: clk_disable_unprepare(i2s->clk); return ret; diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 99ff35e2a25d..35e44e463cfe 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -348,7 +348,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, struct atmel_pcm_dma_params *dma_params; int dir, channels, bits; u32 tfmr, rfmr, tcmr, rcmr; - int start_event; int ret; int fslen, fslen_ext; @@ -457,19 +456,10 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, * The SSC transmit clock is obtained from the BCLK signal on * on the TK line, and the SSC receive clock is * generated from the transmit clock. - * - * For single channel data, one sample is transferred - * on the falling edge of the LRC clock. - * For two channel data, one sample is - * transferred on both edges of the LRC clock. */ - start_event = ((channels == 1) - ? SSC_START_FALLING_RF - : SSC_START_EDGE_RF); - rcmr = SSC_BF(RCMR_PERIOD, 0) | SSC_BF(RCMR_STTDLY, START_DELAY) - | SSC_BF(RCMR_START, start_event) + | SSC_BF(RCMR_START, SSC_START_FALLING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? @@ -478,14 +468,14 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) | SSC_BF(RFMR_FSLEN, 0) - | SSC_BF(RFMR_DATNB, 0) + | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, 0) | SSC_BF(TCMR_STTDLY, START_DELAY) - | SSC_BF(TCMR_START, start_event) + | SSC_BF(TCMR_START, SSC_START_FALLING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ? @@ -495,7 +485,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) | SSC_BF(TFMR_FSLEN, 0) - | SSC_BF(TFMR_DATNB, 0) + | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); @@ -512,7 +502,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) | SSC_BF(RCMR_STTDLY, 1) | SSC_BF(RCMR_START, SSC_START_RISING_RF) - | SSC_BF(RCMR_CKI, SSC_CKI_RISING) + | SSC_BF(RCMR_CKI, SSC_CKI_FALLING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, SSC_CKS_DIV); @@ -527,7 +517,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) | SSC_BF(TCMR_STTDLY, 1) | SSC_BF(TCMR_START, SSC_START_RISING_RF) - | SSC_BF(TCMR_CKI, SSC_CKI_RISING) + | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) | SSC_BF(TCMR_CKS, SSC_CKS_DIV); @@ -556,7 +546,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, rcmr = SSC_BF(RCMR_PERIOD, 0) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_RISING_RF) - | SSC_BF(RCMR_CKI, SSC_CKI_RISING) + | SSC_BF(RCMR_CKI, SSC_CKI_FALLING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_PIN : SSC_CKS_CLOCK); diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index d0547fa275fc..dcdfac0ffeb1 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -46,6 +46,8 @@ static int pcm512x_i2c_remove(struct i2c_client *i2c) static const struct i2c_device_id pcm512x_i2c_id[] = { { "pcm5121", }, { "pcm5122", }, + { "pcm5141", }, + { "pcm5142", }, { } }; MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); @@ -53,6 +55,8 @@ MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5121", }, { .compatible = "ti,pcm5122", }, + { .compatible = "ti,pcm5141", }, + { .compatible = "ti,pcm5142", }, { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index f297058c0038..7b64a9cef704 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -43,6 +43,8 @@ static int pcm512x_spi_remove(struct spi_device *spi) static const struct spi_device_id pcm512x_spi_id[] = { { "pcm5121", }, { "pcm5122", }, + { "pcm5141", }, + { "pcm5142", }, { }, }; MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); @@ -50,6 +52,8 @@ MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5121", }, { .compatible = "ti,pcm5122", }, + { .compatible = "ti,pcm5141", }, + { .compatible = "ti,pcm5142", }, { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index e5f2fb884bf3..9974f201a08f 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -21,12 +21,19 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/gcd.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <sound/pcm_params.h> #include <sound/tlv.h> #include "pcm512x.h" +#define DIV_ROUND_DOWN_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) +#define DIV_ROUND_CLOSEST_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) + #define PCM512x_NUM_SUPPLIES 3 static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = { "AVDD", @@ -39,6 +46,14 @@ struct pcm512x_priv { struct clk *sclk; struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; + int fmt; + int pll_in; + int pll_out; + int pll_r; + int pll_j; + int pll_d; + int pll_p; + unsigned long real_pll; }; /* @@ -69,6 +84,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_MUTE, 0x00 }, { PCM512x_DSP, 0x00 }, { PCM512x_PLL_REF, 0x00 }, + { PCM512x_DAC_REF, 0x00 }, { PCM512x_DAC_ROUTING, 0x11 }, { PCM512x_DSP_PROGRAM, 0x01 }, { PCM512x_CLKDET, 0x00 }, @@ -87,6 +103,25 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, { PCM512x_VCOM_CTRL_1, 0x00 }, { PCM512x_VCOM_CTRL_2, 0x01 }, + { PCM512x_BCLK_LRCLK_CFG, 0x00 }, + { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_GPIO_DACIN, 0x00 }, + { PCM512x_GPIO_PLLIN, 0x00 }, + { PCM512x_SYNCHRONIZE, 0x10 }, + { PCM512x_PLL_COEFF_0, 0x00 }, + { PCM512x_PLL_COEFF_1, 0x00 }, + { PCM512x_PLL_COEFF_2, 0x00 }, + { PCM512x_PLL_COEFF_3, 0x00 }, + { PCM512x_PLL_COEFF_4, 0x00 }, + { PCM512x_DSP_CLKDIV, 0x00 }, + { PCM512x_DAC_CLKDIV, 0x00 }, + { PCM512x_NCP_CLKDIV, 0x00 }, + { PCM512x_OSR_CLKDIV, 0x00 }, + { PCM512x_MASTER_CLKDIV_1, 0x00 }, + { PCM512x_MASTER_CLKDIV_2, 0x00 }, + { PCM512x_FS_SPEED_MODE, 0x00 }, + { PCM512x_IDAC_1, 0x01 }, + { PCM512x_IDAC_2, 0x00 }, }; static bool pcm512x_readable(struct device *dev, unsigned int reg) @@ -103,6 +138,10 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_DSP_GPIO_INPUT: case PCM512x_MASTER_MODE: case PCM512x_PLL_REF: + case PCM512x_DAC_REF: + case PCM512x_GPIO_DACIN: + case PCM512x_GPIO_PLLIN: + case PCM512x_SYNCHRONIZE: case PCM512x_PLL_COEFF_0: case PCM512x_PLL_COEFF_1: case PCM512x_PLL_COEFF_2: @@ -143,6 +182,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_RATE_DET_2: case PCM512x_RATE_DET_3: case PCM512x_RATE_DET_4: + case PCM512x_CLOCK_STATUS: case PCM512x_ANALOG_MUTE_DET: case PCM512x_GPIN: case PCM512x_DIGITAL_MUTE_DET: @@ -154,6 +194,8 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_VCOM_CTRL_1: case PCM512x_VCOM_CTRL_2: case PCM512x_CRAM_CTRL: + case PCM512x_FLEX_A: + case PCM512x_FLEX_B: return true; default: /* There are 256 raw register addresses */ @@ -170,6 +212,7 @@ static bool pcm512x_volatile(struct device *dev, unsigned int reg) case PCM512x_RATE_DET_2: case PCM512x_RATE_DET_3: case PCM512x_RATE_DET_4: + case PCM512x_CLOCK_STATUS: case PCM512x_ANALOG_MUTE_DET: case PCM512x_GPIN: case PCM512x_DIGITAL_MUTE_DET: @@ -188,8 +231,8 @@ static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0); static const char * const pcm512x_dsp_program_texts[] = { "FIR interpolation with de-emphasis", "Low latency IIR with de-emphasis", - "Fixed process flow", "High attenuation with de-emphasis", + "Fixed process flow", "Ringing-less low latency FIR", }; @@ -277,7 +320,7 @@ SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r), SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_ACTL_SHIFT, 1, 0), SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT, - PCM512x_AMLR_SHIFT, 1, 0), + PCM512x_AMRE_SHIFT, 1, 0), SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf), SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds), @@ -303,6 +346,136 @@ static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { { "OUTR", NULL, "DACR" }, }; +static const u32 pcm512x_dai_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000, 384000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_slave = { + .count = ARRAY_SIZE(pcm512x_dai_rates), + .list = pcm512x_dai_rates, +}; + +static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval ranges[2]; + int frame_size; + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) + return frame_size; + + switch (frame_size) { + case 32: + /* No hole when the frame size is 32. */ + return 0; + case 48: + case 64: + /* There is only one hole in the range of supported + * rates, but it moves with the frame size. + */ + memset(ranges, 0, sizeof(ranges)); + ranges[0].min = 8000; + ranges[0].max = 25000000 / frame_size / 2; + ranges[1].min = DIV_ROUND_UP(16000000, frame_size); + ranges[1].max = 384000; + break; + default: + return -EINVAL; + } + + return snd_interval_ranges(hw_param_interval(params, rule->var), + ARRAY_SIZE(ranges), ranges, 0); +} + +static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + struct device *dev = dai->dev; + struct snd_pcm_hw_constraint_ratnums *constraints_no_pll; + struct snd_ratnum *rats_no_pll; + + if (IS_ERR(pcm512x->sclk)) { + dev_err(dev, "Need SCLK for master mode: %ld\n", + PTR_ERR(pcm512x->sclk)); + return PTR_ERR(pcm512x->sclk); + } + + if (pcm512x->pll_out) + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + pcm512x_hw_rule_rate, + NULL, + SNDRV_PCM_HW_PARAM_FRAME_BITS, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + constraints_no_pll = devm_kzalloc(dev, sizeof(*constraints_no_pll), + GFP_KERNEL); + if (!constraints_no_pll) + return -ENOMEM; + constraints_no_pll->nrats = 1; + rats_no_pll = devm_kzalloc(dev, sizeof(*rats_no_pll), GFP_KERNEL); + if (!rats_no_pll) + return -ENOMEM; + constraints_no_pll->rats = rats_no_pll; + rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64; + rats_no_pll->den_min = 1; + rats_no_pll->den_max = 128; + rats_no_pll->den_step = 1; + + return snd_pcm_hw_constraint_ratnums(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + constraints_no_pll); +} + +static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + struct device *dev = dai->dev; + struct regmap *regmap = pcm512x->regmap; + + if (IS_ERR(pcm512x->sclk)) { + dev_info(dev, "No SCLK, using BCLK: %ld\n", + PTR_ERR(pcm512x->sclk)); + + /* Disable reporting of missing SCLK as an error */ + regmap_update_bits(regmap, PCM512x_ERROR_DETECT, + PCM512x_IDCH, PCM512x_IDCH); + + /* Switch PLL input to BCLK */ + regmap_update_bits(regmap, PCM512x_PLL_REF, + PCM512x_SREF, PCM512x_SREF_BCK); + } + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_slave); +} + +static int pcm512x_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + return pcm512x_dai_startup_master(substream, dai); + + case SND_SOC_DAIFMT_CBS_CFS: + return pcm512x_dai_startup_slave(substream, dai); + + default: + return -EINVAL; + } +} + static int pcm512x_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { @@ -340,17 +513,717 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec, return 0; } +static unsigned long pcm512x_find_sck(struct snd_soc_dai *dai, + unsigned long bclk_rate) +{ + struct device *dev = dai->dev; + unsigned long sck_rate; + int pow2; + + /* 64 MHz <= pll_rate <= 100 MHz, VREF mode */ + /* 16 MHz <= sck_rate <= 25 MHz, VREF mode */ + + /* select sck_rate as a multiple of bclk_rate but still with + * as many factors of 2 as possible, as that makes it easier + * to find a fast DAC rate + */ + pow2 = 1 << fls((25000000 - 16000000) / bclk_rate); + for (; pow2; pow2 >>= 1) { + sck_rate = rounddown(25000000, bclk_rate * pow2); + if (sck_rate >= 16000000) + break; + } + if (!pow2) { + dev_err(dev, "Impossible to generate a suitable SCK\n"); + return 0; + } + + dev_dbg(dev, "sck_rate %lu\n", sck_rate); + return sck_rate; +} + +/* pll_rate = pllin_rate * R * J.D / P + * 1 <= R <= 16 + * 1 <= J <= 63 + * 0 <= D <= 9999 + * 1 <= P <= 15 + * 64 MHz <= pll_rate <= 100 MHz + * if D == 0 + * 1 MHz <= pllin_rate / P <= 20 MHz + * else if D > 0 + * 6.667 MHz <= pllin_rate / P <= 20 MHz + * 4 <= J <= 11 + * R = 1 + */ +static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai, + unsigned long pllin_rate, + unsigned long pll_rate) +{ + struct device *dev = dai->dev; + struct snd_soc_codec *codec = dai->codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + unsigned long common; + int R, J, D, P; + unsigned long K; /* 10000 * J.D */ + unsigned long num; + unsigned long den; + + common = gcd(pll_rate, pllin_rate); + dev_dbg(dev, "pll %lu pllin %lu common %lu\n", + pll_rate, pllin_rate, common); + num = pll_rate / common; + den = pllin_rate / common; + + /* pllin_rate / P (or here, den) cannot be greater than 20 MHz */ + if (pllin_rate / den > 20000000 && num < 8) { + num *= 20000000 / (pllin_rate / den); + den *= 20000000 / (pllin_rate / den); + } + dev_dbg(dev, "num / den = %lu / %lu\n", num, den); + + P = den; + if (den <= 15 && num <= 16 * 63 + && 1000000 <= pllin_rate / P && pllin_rate / P <= 20000000) { + /* Try the case with D = 0 */ + D = 0; + /* factor 'num' into J and R, such that R <= 16 and J <= 63 */ + for (R = 16; R; R--) { + if (num % R) + continue; + J = num / R; + if (J == 0 || J > 63) + continue; + + dev_dbg(dev, "R * J / P = %d * %d / %d\n", R, J, P); + pcm512x->real_pll = pll_rate; + goto done; + } + /* no luck */ + } + + R = 1; + + if (num > 0xffffffffUL / 10000) + goto fallback; + + /* Try to find an exact pll_rate using the D > 0 case */ + common = gcd(10000 * num, den); + num = 10000 * num / common; + den /= common; + dev_dbg(dev, "num %lu den %lu common %lu\n", num, den, common); + + for (P = den; P <= 15; P++) { + if (pllin_rate / P < 6667000 || 200000000 < pllin_rate / P) + continue; + if (num * P % den) + continue; + K = num * P / den; + /* J == 12 is ok if D == 0 */ + if (K < 40000 || K > 120000) + continue; + + J = K / 10000; + D = K % 10000; + dev_dbg(dev, "J.D / P = %d.%04d / %d\n", J, D, P); + pcm512x->real_pll = pll_rate; + goto done; + } + + /* Fall back to an approximate pll_rate */ + +fallback: + /* find smallest possible P */ + P = DIV_ROUND_UP(pllin_rate, 20000000); + if (!P) + P = 1; + else if (P > 15) { + dev_err(dev, "Need a slower clock as pll-input\n"); + return -EINVAL; + } + if (pllin_rate / P < 6667000) { + dev_err(dev, "Need a faster clock as pll-input\n"); + return -EINVAL; + } + K = DIV_ROUND_CLOSEST_ULL(10000ULL * pll_rate * P, pllin_rate); + if (K < 40000) + K = 40000; + /* J == 12 is ok if D == 0 */ + if (K > 120000) + K = 120000; + J = K / 10000; + D = K % 10000; + dev_dbg(dev, "J.D / P ~ %d.%04d / %d\n", J, D, P); + pcm512x->real_pll = DIV_ROUND_DOWN_ULL((u64)K * pllin_rate, 10000 * P); + +done: + pcm512x->pll_r = R; + pcm512x->pll_j = J; + pcm512x->pll_d = D; + pcm512x->pll_p = P; + return 0; +} + +static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai, + unsigned long osr_rate, + unsigned long pllin_rate) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + unsigned long dac_rate; + + if (!pcm512x->pll_out) + return 0; /* no PLL to bypass, force SCK as DAC input */ + + if (pllin_rate % osr_rate) + return 0; /* futile, quit early */ + + /* run DAC no faster than 6144000 Hz */ + for (dac_rate = rounddown(6144000, osr_rate); + dac_rate; + dac_rate -= osr_rate) { + + if (pllin_rate / dac_rate > 128) + return 0; /* DAC divider would be too big */ + + if (!(pllin_rate % dac_rate)) + return dac_rate; + + dac_rate -= osr_rate; + } + + return 0; +} + +static int pcm512x_set_dividers(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params) +{ + struct device *dev = dai->dev; + struct snd_soc_codec *codec = dai->codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + unsigned long pllin_rate = 0; + unsigned long pll_rate; + unsigned long sck_rate; + unsigned long mck_rate; + unsigned long bclk_rate; + unsigned long sample_rate; + unsigned long osr_rate; + unsigned long dacsrc_rate; + int bclk_div; + int lrclk_div; + int dsp_div; + int dac_div; + unsigned long dac_rate; + int ncp_div; + int osr_div; + int ret; + int idac; + int fssp; + int gpio; + + lrclk_div = snd_soc_params_to_frame_size(params); + if (lrclk_div == 0) { + dev_err(dev, "No LRCLK?\n"); + return -EINVAL; + } + + if (!pcm512x->pll_out) { + sck_rate = clk_get_rate(pcm512x->sclk); + bclk_div = params->rate_den * 64 / lrclk_div; + bclk_rate = DIV_ROUND_CLOSEST(sck_rate, bclk_div); + + mck_rate = sck_rate; + } else { + ret = snd_soc_params_to_bclk(params); + if (ret < 0) { + dev_err(dev, "Failed to find suitable BCLK: %d\n", ret); + return ret; + } + if (ret == 0) { + dev_err(dev, "No BCLK?\n"); + return -EINVAL; + } + bclk_rate = ret; + + pllin_rate = clk_get_rate(pcm512x->sclk); + + sck_rate = pcm512x_find_sck(dai, bclk_rate); + if (!sck_rate) + return -EINVAL; + pll_rate = 4 * sck_rate; + + ret = pcm512x_find_pll_coeff(dai, pllin_rate, pll_rate); + if (ret != 0) + return ret; + + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_0, pcm512x->pll_p - 1); + if (ret != 0) { + dev_err(dev, "Failed to write PLL P: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_1, pcm512x->pll_j); + if (ret != 0) { + dev_err(dev, "Failed to write PLL J: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_2, pcm512x->pll_d >> 8); + if (ret != 0) { + dev_err(dev, "Failed to write PLL D msb: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_3, pcm512x->pll_d & 0xff); + if (ret != 0) { + dev_err(dev, "Failed to write PLL D lsb: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_4, pcm512x->pll_r - 1); + if (ret != 0) { + dev_err(dev, "Failed to write PLL R: %d\n", ret); + return ret; + } + + mck_rate = pcm512x->real_pll; + + bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate); + } + + if (bclk_div > 128) { + dev_err(dev, "Failed to find BCLK divider\n"); + return -EINVAL; + } + + /* the actual rate */ + sample_rate = sck_rate / bclk_div / lrclk_div; + osr_rate = 16 * sample_rate; + + /* run DSP no faster than 50 MHz */ + dsp_div = mck_rate > 50000000 ? 2 : 1; + + dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate); + if (dac_rate) { + /* the desired clock rate is "compatible" with the pll input + * clock, so use that clock as dac input instead of the pll + * output clock since the pll will introduce jitter and thus + * noise. + */ + dev_dbg(dev, "using pll input as dac input\n"); + ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF, + PCM512x_SDAC, PCM512x_SDAC_GPIO); + if (ret != 0) { + dev_err(codec->dev, + "Failed to set gpio as dacref: %d\n", ret); + return ret; + } + + gpio = PCM512x_GREF_GPIO1 + pcm512x->pll_in - 1; + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_DACIN, + PCM512x_GREF, gpio); + if (ret != 0) { + dev_err(codec->dev, + "Failed to set gpio %d as dacin: %d\n", + pcm512x->pll_in, ret); + return ret; + } + + dacsrc_rate = pllin_rate; + } else { + /* run DAC no faster than 6144000 Hz */ + unsigned long dac_mul = 6144000 / osr_rate; + unsigned long sck_mul = sck_rate / osr_rate; + + for (; dac_mul; dac_mul--) { + if (!(sck_mul % dac_mul)) + break; + } + if (!dac_mul) { + dev_err(dev, "Failed to find DAC rate\n"); + return -EINVAL; + } + + dac_rate = dac_mul * osr_rate; + dev_dbg(dev, "dac_rate %lu sample_rate %lu\n", + dac_rate, sample_rate); + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF, + PCM512x_SDAC, PCM512x_SDAC_SCK); + if (ret != 0) { + dev_err(codec->dev, + "Failed to set sck as dacref: %d\n", ret); + return ret; + } + + dacsrc_rate = sck_rate; + } + + dac_div = DIV_ROUND_CLOSEST(dacsrc_rate, dac_rate); + if (dac_div > 128) { + dev_err(dev, "Failed to find DAC divider\n"); + return -EINVAL; + } + + ncp_div = DIV_ROUND_CLOSEST(dacsrc_rate / dac_div, 1536000); + if (ncp_div > 128 || dacsrc_rate / dac_div / ncp_div > 2048000) { + /* run NCP no faster than 2048000 Hz, but why? */ + ncp_div = DIV_ROUND_UP(dacsrc_rate / dac_div, 2048000); + if (ncp_div > 128) { + dev_err(dev, "Failed to find NCP divider\n"); + return -EINVAL; + } + } + + osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate); + if (osr_div > 128) { + dev_err(dev, "Failed to find OSR divider\n"); + return -EINVAL; + } + + idac = mck_rate / (dsp_div * sample_rate); + + ret = regmap_write(pcm512x->regmap, PCM512x_DSP_CLKDIV, dsp_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write DSP divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_DAC_CLKDIV, dac_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write DAC divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_NCP_CLKDIV, ncp_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write NCP divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_OSR_CLKDIV, osr_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write OSR divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_MASTER_CLKDIV_1, bclk_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write BCLK divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_MASTER_CLKDIV_2, lrclk_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write LRCLK divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_IDAC_1, idac >> 8); + if (ret != 0) { + dev_err(dev, "Failed to write IDAC msb divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_IDAC_2, idac & 0xff); + if (ret != 0) { + dev_err(dev, "Failed to write IDAC lsb divider: %d\n", ret); + return ret; + } + + if (sample_rate <= 48000) + fssp = PCM512x_FSSP_48KHZ; + else if (sample_rate <= 96000) + fssp = PCM512x_FSSP_96KHZ; + else if (sample_rate <= 192000) + fssp = PCM512x_FSSP_192KHZ; + else + fssp = PCM512x_FSSP_384KHZ; + ret = regmap_update_bits(pcm512x->regmap, PCM512x_FS_SPEED_MODE, + PCM512x_FSSP, fssp); + if (ret != 0) { + dev_err(codec->dev, "Failed to set fs speed: %d\n", ret); + return ret; + } + + dev_dbg(codec->dev, "DSP divider %d\n", dsp_div); + dev_dbg(codec->dev, "DAC divider %d\n", dac_div); + dev_dbg(codec->dev, "NCP divider %d\n", ncp_div); + dev_dbg(codec->dev, "OSR divider %d\n", osr_div); + dev_dbg(codec->dev, "BCK divider %d\n", bclk_div); + dev_dbg(codec->dev, "LRCK divider %d\n", lrclk_div); + dev_dbg(codec->dev, "IDAC %d\n", idac); + dev_dbg(codec->dev, "1<<FSSP %d\n", 1 << fssp); + + return 0; +} + +static int pcm512x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + int alen; + int gpio; + int clock_output; + int master_mode; + int ret; + + dev_dbg(codec->dev, "hw_params %u Hz, %u channels\n", + params_rate(params), + params_channels(params)); + + switch (snd_pcm_format_width(params_format(params))) { + case 16: + alen = PCM512x_ALEN_16; + break; + case 20: + alen = PCM512x_ALEN_20; + break; + case 24: + alen = PCM512x_ALEN_24; + break; + case 32: + alen = PCM512x_ALEN_32; + break; + default: + dev_err(codec->dev, "Bad frame size: %d\n", + snd_pcm_format_width(params_format(params))); + return -EINVAL; + } + + switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + ret = regmap_update_bits(pcm512x->regmap, + PCM512x_BCLK_LRCLK_CFG, + PCM512x_BCKP + | PCM512x_BCKO | PCM512x_LRKO, + 0); + if (ret != 0) { + dev_err(codec->dev, + "Failed to enable slave mode: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT, + PCM512x_DCAS, 0); + if (ret != 0) { + dev_err(codec->dev, + "Failed to enable clock divider autoset: %d\n", + ret); + return ret; + } + return 0; + case SND_SOC_DAIFMT_CBM_CFM: + clock_output = PCM512x_BCKO | PCM512x_LRKO; + master_mode = PCM512x_RLRK | PCM512x_RBCK; + break; + case SND_SOC_DAIFMT_CBM_CFS: + clock_output = PCM512x_BCKO; + master_mode = PCM512x_RBCK; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1, + PCM512x_ALEN, alen); + if (ret != 0) { + dev_err(codec->dev, "Failed to set frame size: %d\n", ret); + return ret; + } + + if (pcm512x->pll_out) { + ret = regmap_write(pcm512x->regmap, PCM512x_FLEX_A, 0x11); + if (ret != 0) { + dev_err(codec->dev, "Failed to set FLEX_A: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_FLEX_B, 0xff); + if (ret != 0) { + dev_err(codec->dev, "Failed to set FLEX_B: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT, + PCM512x_IDFS | PCM512x_IDBK + | PCM512x_IDSK | PCM512x_IDCH + | PCM512x_IDCM | PCM512x_DCAS + | PCM512x_IPLK, + PCM512x_IDFS | PCM512x_IDBK + | PCM512x_IDSK | PCM512x_IDCH + | PCM512x_DCAS); + if (ret != 0) { + dev_err(codec->dev, + "Failed to ignore auto-clock failures: %d\n", + ret); + return ret; + } + } else { + ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT, + PCM512x_IDFS | PCM512x_IDBK + | PCM512x_IDSK | PCM512x_IDCH + | PCM512x_IDCM | PCM512x_DCAS + | PCM512x_IPLK, + PCM512x_IDFS | PCM512x_IDBK + | PCM512x_IDSK | PCM512x_IDCH + | PCM512x_DCAS | PCM512x_IPLK); + if (ret != 0) { + dev_err(codec->dev, + "Failed to ignore auto-clock failures: %d\n", + ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN, + PCM512x_PLLE, 0); + if (ret != 0) { + dev_err(codec->dev, "Failed to disable pll: %d\n", ret); + return ret; + } + } + + ret = pcm512x_set_dividers(dai, params); + if (ret != 0) + return ret; + + if (pcm512x->pll_out) { + ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_REF, + PCM512x_SREF, PCM512x_SREF_GPIO); + if (ret != 0) { + dev_err(codec->dev, + "Failed to set gpio as pllref: %d\n", ret); + return ret; + } + + gpio = PCM512x_GREF_GPIO1 + pcm512x->pll_in - 1; + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_PLLIN, + PCM512x_GREF, gpio); + if (ret != 0) { + dev_err(codec->dev, + "Failed to set gpio %d as pllin: %d\n", + pcm512x->pll_in, ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN, + PCM512x_PLLE, PCM512x_PLLE); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable pll: %d\n", ret); + return ret; + } + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG, + PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO, + clock_output); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable clock output: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE, + PCM512x_RLRK | PCM512x_RBCK, + master_mode); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable master mode: %d\n", ret); + return ret; + } + + if (pcm512x->pll_out) { + gpio = PCM512x_G1OE << (pcm512x->pll_out - 1); + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN, + gpio, gpio); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable gpio %d: %d\n", + pcm512x->pll_out, ret); + return ret; + } + + gpio = PCM512x_GPIO_OUTPUT_1 + pcm512x->pll_out - 1; + ret = regmap_update_bits(pcm512x->regmap, gpio, + PCM512x_GxSL, PCM512x_GxSL_PLLCK); + if (ret != 0) { + dev_err(codec->dev, "Failed to output pll on %d: %d\n", + ret, pcm512x->pll_out); + return ret; + } + + gpio = PCM512x_G1OE << (4 - 1); + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN, + gpio, gpio); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable gpio %d: %d\n", + 4, ret); + return ret; + } + + gpio = PCM512x_GPIO_OUTPUT_1 + 4 - 1; + ret = regmap_update_bits(pcm512x->regmap, gpio, + PCM512x_GxSL, PCM512x_GxSL_PLLLK); + if (ret != 0) { + dev_err(codec->dev, + "Failed to output pll lock on %d: %d\n", + ret, 4); + return ret; + } + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE, + PCM512x_RQSY, PCM512x_RQSY_HALT); + if (ret != 0) { + dev_err(codec->dev, "Failed to halt clocks: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE, + PCM512x_RQSY, PCM512x_RQSY_RESUME); + if (ret != 0) { + dev_err(codec->dev, "Failed to resume clocks: %d\n", ret); + return ret; + } + + return 0; +} + +static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + pcm512x->fmt = fmt; + + return 0; +} + +static const struct snd_soc_dai_ops pcm512x_dai_ops = { + .startup = pcm512x_dai_startup, + .hw_params = pcm512x_hw_params, + .set_fmt = pcm512x_set_fmt, +}; + static struct snd_soc_dai_driver pcm512x_dai = { .name = "pcm512x-hifi", .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 384000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE }, + .ops = &pcm512x_dai_ops, }; static struct snd_soc_codec_driver pcm512x_codec_driver = { @@ -448,21 +1321,9 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) } pcm512x->sclk = devm_clk_get(dev, NULL); - if (IS_ERR(pcm512x->sclk)) { - if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - dev_info(dev, "No SCLK, using BCLK: %ld\n", - PTR_ERR(pcm512x->sclk)); - - /* Disable reporting of missing SCLK as an error */ - regmap_update_bits(regmap, PCM512x_ERROR_DETECT, - PCM512x_IDCH, PCM512x_IDCH); - - /* Switch PLL input to BCLK */ - regmap_update_bits(regmap, PCM512x_PLL_REF, - PCM512x_SREF, PCM512x_SREF); - } else { + if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (!IS_ERR(pcm512x->sclk)) { ret = clk_prepare_enable(pcm512x->sclk); if (ret != 0) { dev_err(dev, "Failed to enable SCLK: %d\n", ret); @@ -483,6 +1344,43 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) pm_runtime_enable(dev); pm_runtime_idle(dev); +#ifdef CONFIG_OF + if (dev->of_node) { + const struct device_node *np = dev->of_node; + u32 val; + + if (of_property_read_u32(np, "pll-in", &val) >= 0) { + if (val > 6) { + dev_err(dev, "Invalid pll-in\n"); + ret = -EINVAL; + goto err_clk; + } + pcm512x->pll_in = val; + } + + if (of_property_read_u32(np, "pll-out", &val) >= 0) { + if (val > 6) { + dev_err(dev, "Invalid pll-out\n"); + ret = -EINVAL; + goto err_clk; + } + pcm512x->pll_out = val; + } + + if (!pcm512x->pll_in != !pcm512x->pll_out) { + dev_err(dev, + "Error: both pll-in and pll-out, or none\n"); + ret = -EINVAL; + goto err_clk; + } + if (pcm512x->pll_in && pcm512x->pll_in == pcm512x->pll_out) { + dev_err(dev, "Error: pll-in == pll-out\n"); + ret = -EINVAL; + goto err_clk; + } + } +#endif + ret = snd_soc_register_codec(dev, &pcm512x_codec_driver, &pcm512x_dai, 1); if (ret != 0) { diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h index 6ee76aaca09a..b7c310207223 100644 --- a/sound/soc/codecs/pcm512x.h +++ b/sound/soc/codecs/pcm512x.h @@ -37,6 +37,10 @@ #define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_BASE(0) + 10) #define PCM512x_MASTER_MODE (PCM512x_PAGE_BASE(0) + 12) #define PCM512x_PLL_REF (PCM512x_PAGE_BASE(0) + 13) +#define PCM512x_DAC_REF (PCM512x_PAGE_BASE(0) + 14) +#define PCM512x_GPIO_DACIN (PCM512x_PAGE_BASE(0) + 16) +#define PCM512x_GPIO_PLLIN (PCM512x_PAGE_BASE(0) + 18) +#define PCM512x_SYNCHRONIZE (PCM512x_PAGE_BASE(0) + 19) #define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_BASE(0) + 20) #define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_BASE(0) + 21) #define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_BASE(0) + 22) @@ -77,6 +81,7 @@ #define PCM512x_RATE_DET_2 (PCM512x_PAGE_BASE(0) + 92) #define PCM512x_RATE_DET_3 (PCM512x_PAGE_BASE(0) + 93) #define PCM512x_RATE_DET_4 (PCM512x_PAGE_BASE(0) + 94) +#define PCM512x_CLOCK_STATUS (PCM512x_PAGE_BASE(0) + 95) #define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_BASE(0) + 108) #define PCM512x_GPIN (PCM512x_PAGE_BASE(0) + 119) #define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_BASE(0) + 120) @@ -91,7 +96,10 @@ #define PCM512x_CRAM_CTRL (PCM512x_PAGE_BASE(44) + 1) -#define PCM512x_MAX_REGISTER (PCM512x_PAGE_BASE(44) + 1) +#define PCM512x_FLEX_A (PCM512x_PAGE_BASE(253) + 63) +#define PCM512x_FLEX_B (PCM512x_PAGE_BASE(253) + 64) + +#define PCM512x_MAX_REGISTER (PCM512x_PAGE_BASE(253) + 64) /* Page 0, Register 1 - reset */ #define PCM512x_RSTR (1 << 0) @@ -108,8 +116,8 @@ #define PCM512x_RQML_SHIFT 4 /* Page 0, Register 4 - PLL */ -#define PCM512x_PLCE (1 << 0) -#define PCM512x_RLCE_SHIFT 0 +#define PCM512x_PLLE (1 << 0) +#define PCM512x_PLLE_SHIFT 0 #define PCM512x_PLCK (1 << 4) #define PCM512x_PLCK_SHIFT 4 @@ -119,8 +127,66 @@ #define PCM512x_DEMP (1 << 4) #define PCM512x_DEMP_SHIFT 4 +/* Page 0, Register 8 - GPIO output enable */ +#define PCM512x_G1OE (1 << 0) +#define PCM512x_G2OE (1 << 1) +#define PCM512x_G3OE (1 << 2) +#define PCM512x_G4OE (1 << 3) +#define PCM512x_G5OE (1 << 4) +#define PCM512x_G6OE (1 << 5) + +/* Page 0, Register 9 - BCK, LRCLK configuration */ +#define PCM512x_LRKO (1 << 0) +#define PCM512x_LRKO_SHIFT 0 +#define PCM512x_BCKO (1 << 4) +#define PCM512x_BCKO_SHIFT 4 +#define PCM512x_BCKP (1 << 5) +#define PCM512x_BCKP_SHIFT 5 + +/* Page 0, Register 12 - Master mode BCK, LRCLK reset */ +#define PCM512x_RLRK (1 << 0) +#define PCM512x_RLRK_SHIFT 0 +#define PCM512x_RBCK (1 << 1) +#define PCM512x_RBCK_SHIFT 1 + /* Page 0, Register 13 - PLL reference */ -#define PCM512x_SREF (1 << 4) +#define PCM512x_SREF (7 << 4) +#define PCM512x_SREF_SHIFT 4 +#define PCM512x_SREF_SCK (0 << 4) +#define PCM512x_SREF_BCK (1 << 4) +#define PCM512x_SREF_GPIO (3 << 4) + +/* Page 0, Register 14 - DAC reference */ +#define PCM512x_SDAC (7 << 4) +#define PCM512x_SDAC_SHIFT 4 +#define PCM512x_SDAC_MCK (0 << 4) +#define PCM512x_SDAC_PLL (1 << 4) +#define PCM512x_SDAC_SCK (3 << 4) +#define PCM512x_SDAC_BCK (4 << 4) +#define PCM512x_SDAC_GPIO (5 << 4) + +/* Page 0, Register 16, 18 - GPIO source for DAC, PLL */ +#define PCM512x_GREF (7 << 0) +#define PCM512x_GREF_SHIFT 0 +#define PCM512x_GREF_GPIO1 (0 << 0) +#define PCM512x_GREF_GPIO2 (1 << 0) +#define PCM512x_GREF_GPIO3 (2 << 0) +#define PCM512x_GREF_GPIO4 (3 << 0) +#define PCM512x_GREF_GPIO5 (4 << 0) +#define PCM512x_GREF_GPIO6 (5 << 0) + +/* Page 0, Register 19 - synchronize */ +#define PCM512x_RQSY (1 << 0) +#define PCM512x_RQSY_RESUME (0 << 0) +#define PCM512x_RQSY_HALT (1 << 0) + +/* Page 0, Register 34 - fs speed mode */ +#define PCM512x_FSSP (3 << 0) +#define PCM512x_FSSP_SHIFT 0 +#define PCM512x_FSSP_48KHZ (0 << 0) +#define PCM512x_FSSP_96KHZ (1 << 0) +#define PCM512x_FSSP_192KHZ (2 << 0) +#define PCM512x_FSSP_384KHZ (3 << 0) /* Page 0, Register 37 - Error detection */ #define PCM512x_IPLK (1 << 0) @@ -131,6 +197,20 @@ #define PCM512x_IDBK (1 << 5) #define PCM512x_IDFS (1 << 6) +/* Page 0, Register 40 - I2S configuration */ +#define PCM512x_ALEN (3 << 0) +#define PCM512x_ALEN_SHIFT 0 +#define PCM512x_ALEN_16 (0 << 0) +#define PCM512x_ALEN_20 (1 << 0) +#define PCM512x_ALEN_24 (2 << 0) +#define PCM512x_ALEN_32 (3 << 0) +#define PCM512x_AFMT (3 << 4) +#define PCM512x_AFMT_SHIFT 4 +#define PCM512x_AFMT_I2S (0 << 4) +#define PCM512x_AFMT_DSP (1 << 4) +#define PCM512x_AFMT_RTJ (2 << 4) +#define PCM512x_AFMT_LTJ (3 << 4) + /* Page 0, Register 42 - DAC routing */ #define PCM512x_AUPR_SHIFT 0 #define PCM512x_AUPL_SHIFT 4 @@ -152,7 +232,26 @@ /* Page 0, Register 65 - Digital mute enables */ #define PCM512x_ACTL_SHIFT 2 #define PCM512x_AMLE_SHIFT 1 -#define PCM512x_AMLR_SHIFT 0 +#define PCM512x_AMRE_SHIFT 0 + +/* Page 0, Register 80-85, GPIO output selection */ +#define PCM512x_GxSL (31 << 0) +#define PCM512x_GxSL_SHIFT 0 +#define PCM512x_GxSL_OFF (0 << 0) +#define PCM512x_GxSL_DSP (1 << 0) +#define PCM512x_GxSL_REG (2 << 0) +#define PCM512x_GxSL_AMUTB (3 << 0) +#define PCM512x_GxSL_AMUTL (4 << 0) +#define PCM512x_GxSL_AMUTR (5 << 0) +#define PCM512x_GxSL_CLKI (6 << 0) +#define PCM512x_GxSL_SDOUT (7 << 0) +#define PCM512x_GxSL_ANMUL (8 << 0) +#define PCM512x_GxSL_ANMUR (9 << 0) +#define PCM512x_GxSL_PLLLK (10 << 0) +#define PCM512x_GxSL_CPCLK (11 << 0) +#define PCM512x_GxSL_UV0_7 (14 << 0) +#define PCM512x_GxSL_UV0_3 (15 << 0) +#define PCM512x_GxSL_PLLCK (16 << 0) /* Page 1, Register 2 - analog volume control */ #define PCM512x_RAGN_SHIFT 0 diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 8104d2285602..d0698891b69e 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -305,6 +305,8 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) *hp = false; *mic = false; + if (!rt286->codec) + return -EINVAL; if (rt286->pdata.cbj_en) { regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf); *hp = buf & 0x80000000; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index b386958de042..178e55d4d481 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2125,6 +2125,7 @@ MODULE_DEVICE_TABLE(of, rt5640_of_match); static struct acpi_device_id rt5640_acpi_match[] = { { "INT33CA", 0 }, { "10EC5640", 0 }, + { "10EC5642", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match); diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 697aabf456a5..f5b054de481e 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -14,10 +14,12 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/acpi.h> #include <linux/spi/spi.h> +#include <linux/dmi.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -2190,6 +2192,13 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, if (freq == rt5670->sysclk && clk_id == rt5670->sysclk_src) return 0; + if (rt5670->pdata.jd_mode) { + if (clk_id == RT5670_SCLK_S_PLL1) + snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1"); + else + snd_soc_dapm_disable_pin(&codec->dapm, "PLL1"); + snd_soc_dapm_sync(&codec->dapm); + } switch (clk_id) { case RT5670_SCLK_S_MCLK: reg_val |= RT5670_SCLK_SRC_MCLK; @@ -2551,6 +2560,17 @@ static struct acpi_device_id rt5670_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match); #endif +static const struct dmi_system_id dmi_platform_intel_braswell[] = { + { + .ident = "Intel Braswell", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Braswell CRB"), + }, + }, + {} +}; + static int rt5670_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -2570,6 +2590,12 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, if (pdata) rt5670->pdata = *pdata; + if (dmi_check_system(dmi_platform_intel_braswell)) { + rt5670->pdata.dmic_en = true; + rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; + rt5670->pdata.jd_mode = 1; + } + rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap); if (IS_ERR(rt5670->regmap)) { ret = PTR_ERR(rt5670->regmap); @@ -2611,6 +2637,10 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, } if (rt5670->pdata.jd_mode) { + regmap_update_bits(rt5670->regmap, RT5670_GLB_CLK, + RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_RCCLK); + rt5670->sysclk = 0; + rt5670->sysclk_src = RT5670_SCLK_S_RCCLK; regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1, RT5670_PWR_MB, RT5670_PWR_MB); regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG2, @@ -2718,18 +2748,26 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, } + pm_runtime_enable(&i2c->dev); + pm_request_idle(&i2c->dev); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5670, rt5670_dai, ARRAY_SIZE(rt5670_dai)); if (ret < 0) goto err; + pm_runtime_put(&i2c->dev); + return 0; err: + pm_runtime_disable(&i2c->dev); + return ret; } static int rt5670_i2c_remove(struct i2c_client *i2c) { + pm_runtime_disable(&i2c->dev); snd_soc_unregister_codec(&i2c->dev); return 0; diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 8f0bd86481d3..26fc538f03b1 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -784,8 +784,8 @@ static unsigned int bst_tlv[] = { static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = rt5677->dsp_vad_en; @@ -795,8 +795,9 @@ static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol, static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0]; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 6d6ba3a1d25a..e182e6569bbd 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -485,21 +485,21 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* setting i2s data format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_A: - i2sctl |= SGTL5000_I2S_MODE_PCM; + i2sctl |= SGTL5000_I2S_MODE_PCM << SGTL5000_I2S_MODE_SHIFT; break; case SND_SOC_DAIFMT_DSP_B: - i2sctl |= SGTL5000_I2S_MODE_PCM; + i2sctl |= SGTL5000_I2S_MODE_PCM << SGTL5000_I2S_MODE_SHIFT; i2sctl |= SGTL5000_I2S_LRALIGN; break; case SND_SOC_DAIFMT_I2S: - i2sctl |= SGTL5000_I2S_MODE_I2S_LJ; + i2sctl |= SGTL5000_I2S_MODE_I2S_LJ << SGTL5000_I2S_MODE_SHIFT; break; case SND_SOC_DAIFMT_RIGHT_J: - i2sctl |= SGTL5000_I2S_MODE_RJ; + i2sctl |= SGTL5000_I2S_MODE_RJ << SGTL5000_I2S_MODE_SHIFT; i2sctl |= SGTL5000_I2S_LRPOL; break; case SND_SOC_DAIFMT_LEFT_J: - i2sctl |= SGTL5000_I2S_MODE_I2S_LJ; + i2sctl |= SGTL5000_I2S_MODE_I2S_LJ << SGTL5000_I2S_MODE_SHIFT; i2sctl |= SGTL5000_I2S_LRALIGN; break; default: @@ -1464,6 +1464,9 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, if (ret) return ret; + /* Need 8 clocks before I2C accesses */ + udelay(1); + /* read chip information */ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); if (ret) diff --git a/sound/soc/codecs/sta32x.h b/sound/soc/codecs/sta32x.h index d8e32a6262ee..d3191c983d71 100644 --- a/sound/soc/codecs/sta32x.h +++ b/sound/soc/codecs/sta32x.h @@ -131,7 +131,7 @@ #define STA32X_CONFF_OCFG_MASK 0x03 #define STA32X_CONFF_OCFG_SHIFT 0 #define STA32X_CONFF_IDE 0x04 -#define STA32X_CONFF_IDE_SHIFT 3 +#define STA32X_CONFF_IDE_SHIFT 2 #define STA32X_CONFF_BCLE 0x08 #define STA32X_CONFF_ECLE 0x20 #define STA32X_CONFF_PWDN 0x40 diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index a78ad361f494..07603d142923 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1046,7 +1046,7 @@ static int aic3x_prepare(struct snd_pcm_substream *substream, delay += aic3x->tdm_delay; /* Configure data delay */ - snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, aic3x->tdm_delay); + snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay); return 0; } diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 1d1205702d23..9f2dced046de 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -254,6 +254,7 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c, struct ts3a227e *ts3a227e; struct device *dev = &i2c->dev; int ret; + unsigned int acc_reg; ts3a227e = devm_kzalloc(&i2c->dev, sizeof(*ts3a227e), GFP_KERNEL); if (ts3a227e == NULL) @@ -283,6 +284,11 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c, INTB_DISABLE | ADC_COMPLETE_INT_DISABLE, ADC_COMPLETE_INT_DISABLE); + /* Read jack status because chip might not trigger interrupt at boot. */ + regmap_read(ts3a227e->regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg); + ts3a227e_new_jack_state(ts3a227e, acc_reg); + ts3a227e_jack_report(ts3a227e); + return 0; } diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index e129c81ad219..098c143f44d6 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -718,6 +718,8 @@ static int wm8731_i2c_probe(struct i2c_client *i2c, if (wm8731 == NULL) return -ENOMEM; + mutex_init(&wm8731->lock); + wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); if (IS_ERR(wm8731->regmap)) { ret = PTR_ERR(wm8731->regmap); diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index f6847fdd6ddd..eb0a1644ba11 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -323,7 +323,7 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("ROUT2"), SND_SOC_DAPM_OUTPUT("MONO1"), SND_SOC_DAPM_OUTPUT("OUT3"), - SND_SOC_DAPM_OUTPUT("VREF"), + SND_SOC_DAPM_VMID("VREF"), SND_SOC_DAPM_INPUT("LINPUT1"), SND_SOC_DAPM_INPUT("LINPUT2"), diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index a0f8a6263660..c5eaa0198ef0 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1076,10 +1076,13 @@ static const struct snd_soc_dapm_route adc_intercon[] = { { "Right Capture PGA", NULL, "Right Capture Mux" }, { "Right Capture PGA", NULL, "Right Capture Inverting Mux" }, - { "AIFOUTL", "Left", "ADCL" }, - { "AIFOUTL", "Right", "ADCR" }, - { "AIFOUTR", "Left", "ADCL" }, - { "AIFOUTR", "Right", "ADCR" }, + { "AIFOUTL Mux", "Left", "ADCL" }, + { "AIFOUTL Mux", "Right", "ADCR" }, + { "AIFOUTR Mux", "Left", "ADCL" }, + { "AIFOUTR Mux", "Right", "ADCR" }, + + { "AIFOUTL", NULL, "AIFOUTL Mux" }, + { "AIFOUTR", NULL, "AIFOUTR Mux" }, { "ADCL", NULL, "CLK_DSP" }, { "ADCL", NULL, "Left Capture PGA" }, @@ -1089,12 +1092,16 @@ static const struct snd_soc_dapm_route adc_intercon[] = { }; static const struct snd_soc_dapm_route dac_intercon[] = { - { "DACL", "Right", "AIFINR" }, - { "DACL", "Left", "AIFINL" }, + { "DACL Mux", "Left", "AIFINL" }, + { "DACL Mux", "Right", "AIFINR" }, + + { "DACR Mux", "Left", "AIFINL" }, + { "DACR Mux", "Right", "AIFINR" }, + + { "DACL", NULL, "DACL Mux" }, { "DACL", NULL, "CLK_DSP" }, - { "DACR", "Right", "AIFINR" }, - { "DACR", "Left", "AIFINL" }, + { "DACR", NULL, "DACR Mux" }, { "DACR", NULL, "CLK_DSP" }, { "Charge pump", NULL, "SYSCLK" }, diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 031a1ae71d94..a96eb497a379 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -556,7 +556,7 @@ static struct { { 22050, 2 }, { 24000, 2 }, { 16000, 3 }, - { 11250, 4 }, + { 11025, 4 }, { 12000, 4 }, { 8000, 5 }, }; diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 3eddb18fefd1..5cc457ef8894 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -344,23 +344,27 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) struct snd_ac97 *ac97; int ret = 0; - ac97 = snd_soc_new_ac97_codec(codec); + ac97 = snd_soc_alloc_ac97_codec(codec); if (IS_ERR(ac97)) { ret = PTR_ERR(ac97); dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; } - snd_soc_codec_set_drvdata(codec, ac97); - ret = wm9705_reset(codec); if (ret) - goto reset_err; + goto err_put_device; + + ret = device_add(&ac97->dev); + if (ret) + goto err_put_device; + + snd_soc_codec_set_drvdata(codec, ac97); return 0; -reset_err: - snd_soc_free_ac97_codec(ac97); +err_put_device: + put_device(&ac97->dev); return ret; } diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index e04643d2bb24..9517571e820d 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -666,7 +666,7 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); int ret = 0; - wm9712->ac97 = snd_soc_new_ac97_codec(codec); + wm9712->ac97 = snd_soc_alloc_ac97_codec(codec); if (IS_ERR(wm9712->ac97)) { ret = PTR_ERR(wm9712->ac97); dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret); @@ -675,15 +675,19 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) ret = wm9712_reset(codec, 0); if (ret < 0) - goto reset_err; + goto err_put_device; + + ret = device_add(&wm9712->ac97->dev); + if (ret) + goto err_put_device; /* set alc mux to none */ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); return 0; -reset_err: - snd_soc_free_ac97_codec(wm9712->ac97); +err_put_device: + put_device(&wm9712->ac97->dev); return ret; } diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index e5807de796cd..68222917b396 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1225,7 +1225,7 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); int ret = 0, reg; - wm9713->ac97 = snd_soc_new_ac97_codec(codec); + wm9713->ac97 = snd_soc_alloc_ac97_codec(codec); if (IS_ERR(wm9713->ac97)) return PTR_ERR(wm9713->ac97); @@ -1234,7 +1234,11 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) wm9713_reset(codec, 0); ret = wm9713_reset(codec, 1); if (ret < 0) - goto reset_err; + goto err_put_device; + + ret = device_add(&wm9713->ac97->dev); + if (ret) + goto err_put_device; /* unmute the adc - move to kcontrol */ reg = ac97_read(codec, AC97_CD) & 0x7fff; @@ -1242,8 +1246,8 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) return 0; -reset_err: - snd_soc_free_ac97_codec(wm9713->ac97); +err_put_device: + put_device(&wm9713->ac97->dev); return ret; } diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index b93168d4f648..06d3a34ac90a 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -209,16 +209,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, switch (config->chan_nr) { case EIGHT_CHANNEL_SUPPORT: - ch_reg = 3; - break; case SIX_CHANNEL_SUPPORT: - ch_reg = 2; - break; case FOUR_CHANNEL_SUPPORT: - ch_reg = 1; - break; case TWO_CHANNEL_SUPPORT: - ch_reg = 0; break; default: dev_err(dev->dev, "channel not supported\n"); @@ -227,18 +220,22 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, i2s_disable_channels(dev, substream->stream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - i2s_write_reg(dev->i2s_base, TCR(ch_reg), xfer_resolution); - i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); - i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); - } else { - i2s_write_reg(dev->i2s_base, RCR(ch_reg), xfer_resolution); - i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); - i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); + for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_base, TCR(ch_reg), + xfer_resolution); + i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); + i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); + } else { + i2s_write_reg(dev->i2s_base, RCR(ch_reg), + xfer_resolution); + i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); + i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); + } } i2s_write_reg(dev->i2s_base, CCR, ccr); @@ -263,6 +260,19 @@ static void dw_i2s_shutdown(struct snd_pcm_substream *substream, snd_soc_dai_set_dma_data(dai, substream, NULL); } +static int dw_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s_write_reg(dev->i2s_base, TXFFR, 1); + else + i2s_write_reg(dev->i2s_base, RXFFR, 1); + + return 0; +} + static int dw_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -294,6 +304,7 @@ static struct snd_soc_dai_ops dw_i2s_dai_ops = { .startup = dw_i2s_startup, .shutdown = dw_i2s_shutdown, .hw_params = dw_i2s_hw_params, + .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, }; @@ -324,13 +335,47 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) #define dw_i2s_resume NULL #endif +static void dw_configure_dai_by_pd(struct dw_i2s_dev *dev, + struct snd_soc_dai_driver *dw_i2s_dai, + struct resource *res, + const struct i2s_platform_data *pdata) +{ + /* Set DMA slaves info */ + + dev->play_dma_data.data = pdata->play_dma_data; + dev->capture_dma_data.data = pdata->capture_dma_data; + dev->play_dma_data.addr = res->start + I2S_TXDMA; + dev->capture_dma_data.addr = res->start + I2S_RXDMA; + dev->play_dma_data.max_burst = 16; + dev->capture_dma_data.max_burst = 16; + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->play_dma_data.filter = pdata->filter; + dev->capture_dma_data.filter = pdata->filter; + + if (pdata->cap & DWC_I2S_PLAY) { + dev_dbg(dev->dev, " designware: play supported\n"); + dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; + dw_i2s_dai->playback.channels_max = pdata->channel; + dw_i2s_dai->playback.formats = pdata->snd_fmts; + dw_i2s_dai->playback.rates = pdata->snd_rates; + } + + if (pdata->cap & DWC_I2S_RECORD) { + dev_dbg(dev->dev, "designware: record supported\n"); + dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; + dw_i2s_dai->capture.channels_max = pdata->channel; + dw_i2s_dai->capture.formats = pdata->snd_fmts; + dw_i2s_dai->capture.rates = pdata->snd_rates; + } +} + static int dw_i2s_probe(struct platform_device *pdev) { const struct i2s_platform_data *pdata = pdev->dev.platform_data; struct dw_i2s_dev *dev; struct resource *res; int ret; - unsigned int cap; struct snd_soc_dai_driver *dw_i2s_dai; if (!pdata) { @@ -345,44 +390,23 @@ static int dw_i2s_probe(struct platform_device *pdev) } dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); - if (!dw_i2s_dai) { - dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); + if (!dw_i2s_dai) return -ENOMEM; - } dw_i2s_dai->ops = &dw_i2s_dai_ops; dw_i2s_dai->suspend = dw_i2s_suspend; dw_i2s_dai->resume = dw_i2s_resume; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no i2s resource defined\n"); - return -ENODEV; - } - dev->i2s_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dev->i2s_base)) { - dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + if (IS_ERR(dev->i2s_base)) return PTR_ERR(dev->i2s_base); - } - - cap = pdata->cap; - dev->capability = cap; - dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - /* Set DMA slaves info */ - - dev->play_dma_data.data = pdata->play_dma_data; - dev->capture_dma_data.data = pdata->capture_dma_data; - dev->play_dma_data.addr = res->start + I2S_TXDMA; - dev->capture_dma_data.addr = res->start + I2S_RXDMA; - dev->play_dma_data.max_burst = 16; - dev->capture_dma_data.max_burst = 16; - dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - dev->play_dma_data.filter = pdata->filter; - dev->capture_dma_data.filter = pdata->filter; + dev->dev = &pdev->dev; + dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); + dev->capability = pdata->cap; + dev->i2s_clk_cfg = pdata->i2s_clk_cfg; dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); @@ -391,23 +415,6 @@ static int dw_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_clk_put; - if (cap & DWC_I2S_PLAY) { - dev_dbg(&pdev->dev, " designware: play supported\n"); - dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->playback.channels_max = pdata->channel; - dw_i2s_dai->playback.formats = pdata->snd_fmts; - dw_i2s_dai->playback.rates = pdata->snd_rates; - } - - if (cap & DWC_I2S_RECORD) { - dev_dbg(&pdev->dev, "designware: record supported\n"); - dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->capture.channels_max = pdata->channel; - dw_i2s_dai->capture.formats = pdata->snd_fmts; - dw_i2s_dai->capture.rates = pdata->snd_rates; - } - - dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, dw_i2s_dai, 1); diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h index 91a550f4a10d..5e793bbb6b02 100644 --- a/sound/soc/fsl/fsl_esai.h +++ b/sound/soc/fsl/fsl_esai.h @@ -302,7 +302,7 @@ #define ESAI_xCCR_xFP_MASK (((1 << ESAI_xCCR_xFP_WIDTH) - 1) << ESAI_xCCR_xFP_SHIFT) #define ESAI_xCCR_xFP(v) ((((v) - 1) << ESAI_xCCR_xFP_SHIFT) & ESAI_xCCR_xFP_MASK) #define ESAI_xCCR_xDC_SHIFT 9 -#define ESAI_xCCR_xDC_WIDTH 4 +#define ESAI_xCCR_xDC_WIDTH 5 #define ESAI_xCCR_xDC_MASK (((1 << ESAI_xCCR_xDC_WIDTH) - 1) << ESAI_xCCR_xDC_SHIFT) #define ESAI_xCCR_xDC(v) ((((v) - 1) << ESAI_xCCR_xDC_SHIFT) & ESAI_xCCR_xDC_MASK) #define ESAI_xCCR_xPSR_SHIFT 8 diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index a65f17d57ffb..059496ed9ad7 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1362,9 +1362,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) } ssi_private->irq = platform_get_irq(pdev, 0); - if (!ssi_private->irq) { + if (ssi_private->irq < 0) { dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); - return -ENXIO; + return ssi_private->irq; } /* Are the RX and the TX clocks locked? */ diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index 4caacb05a623..cd146d4fa805 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -257,6 +257,7 @@ static int imx_wm8962_probe(struct platform_device *pdev) if (ret) goto clk_fail; data->card.num_links = 1; + data->card.owner = THIS_MODULE; data->card.dai_link = &data->dai; data->card.dapm_widgets = imx_wm8962_dapm_widgets; data->card.num_dapm_widgets = ARRAY_SIZE(imx_wm8962_dapm_widgets); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index fb9240fdc9b7..7fe3009b1c43 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -452,9 +452,8 @@ static int asoc_simple_card_parse_of(struct device_node *node, } /* Decrease the reference count of the device nodes */ -static int asoc_simple_card_unref(struct platform_device *pdev) +static int asoc_simple_card_unref(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(pdev); struct snd_soc_dai_link *dai_link; int num_links; @@ -556,7 +555,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) return ret; err: - asoc_simple_card_unref(pdev); + asoc_simple_card_unref(&priv->snd_card); return ret; } @@ -572,7 +571,7 @@ static int asoc_simple_card_remove(struct platform_device *pdev) snd_soc_jack_free_gpios(&simple_card_mic_jack, 1, &simple_card_mic_jack_gpio); - return asoc_simple_card_unref(pdev); + return asoc_simple_card_unref(card); } static const struct of_device_id asoc_simple_of_match[] = { diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index e989ecf046c9..c0813f546d1f 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -46,7 +46,7 @@ config SND_SOC_INTEL_BAYTRAIL config SND_SOC_INTEL_HASWELL_MACH tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" - depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \\ + depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \ I2C_DESIGNWARE_PLATFORM select SND_SOC_INTEL_HASWELL select SND_SOC_RT5640 @@ -76,7 +76,7 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH config SND_SOC_INTEL_BROADWELL_MACH tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" - depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \\ + depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \ I2C_DESIGNWARE_PLATFORM select SND_SOC_INTEL_HASWELL select SND_COMPRESS_OFFLOAD @@ -89,7 +89,7 @@ config SND_SOC_INTEL_BROADWELL_MACH config SND_SOC_INTEL_BYTCR_RT5640_MACH tristate "ASoC Audio DSP Support for MID BYT Platform" - depends on X86 + depends on X86 && I2C select SND_SOC_RT5640 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI @@ -101,7 +101,7 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH config SND_SOC_INTEL_CHT_BSW_RT5672_MACH tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec" - depends on X86_INTEL_LPSS + depends on X86_INTEL_LPSS && I2C select SND_SOC_RT5670 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/bytcr_dpcm_rt5640.c index f5d0fc1ab10c..59308629043e 100644 --- a/sound/soc/intel/bytcr_dpcm_rt5640.c +++ b/sound/soc/intel/bytcr_dpcm_rt5640.c @@ -215,7 +215,6 @@ static int snd_byt_mc_probe(struct platform_device *pdev) static struct platform_driver snd_byt_mc_driver = { .driver = { - .owner = THIS_MODULE, .name = "bytt100_rt5640", .pm = &snd_soc_pm_ops, }, @@ -227,4 +226,4 @@ module_platform_driver(snd_byt_mc_driver); MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:bytrt5640-audio"); +MODULE_ALIAS("platform:bytt100_rt5640"); diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/cht_bsw_rt5672.c index 9b8b561171b7..a406c6104897 100644 --- a/sound/soc/intel/cht_bsw_rt5672.c +++ b/sound/soc/intel/cht_bsw_rt5672.c @@ -270,7 +270,6 @@ static int snd_cht_mc_probe(struct platform_device *pdev) static struct platform_driver snd_cht_mc_driver = { .driver = { - .owner = THIS_MODULE, .name = "cht-bsw-rt5672", .pm = &snd_soc_pm_ops, }, diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index 4a5bde9c686b..a2ae2c5f2e9f 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -497,6 +497,7 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw, sst_module->sst_fw = sst_fw; sst_module->scratch_size = template->scratch_size; sst_module->persistent_size = template->persistent_size; + sst_module->entry = template->entry; INIT_LIST_HEAD(&sst_module->block_list); INIT_LIST_HEAD(&sst_module->runtime_list); @@ -706,6 +707,7 @@ static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba struct list_head *block_list) { struct sst_mem_block *block, *tmp; + struct sst_block_allocator ba_tmp = *ba; u32 end = ba->offset + ba->size, block_end; int err; @@ -730,9 +732,9 @@ static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba if (ba->offset >= block->offset && ba->offset < block_end) { /* align ba to block boundary */ - ba->size -= block_end - ba->offset; - ba->offset = block_end; - err = block_alloc_contiguous(dsp, ba, block_list); + ba_tmp.size -= block_end - ba->offset; + ba_tmp.offset = block_end; + err = block_alloc_contiguous(dsp, &ba_tmp, block_list); if (err < 0) return -ENOMEM; @@ -763,10 +765,14 @@ static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba /* does block span more than 1 section */ if (ba->offset >= block->offset && ba->offset < block_end) { + /* add block */ + list_move(&block->list, &dsp->used_block_list); + list_add(&block->module_list, block_list); /* align ba to block boundary */ - ba->offset = block->offset; + ba_tmp.size -= block_end - ba->offset; + ba_tmp.offset = block_end; - err = block_alloc_contiguous(dsp, ba, block_list); + err = block_alloc_contiguous(dsp, &ba_tmp, block_list); if (err < 0) return -ENOMEM; diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 3f8c48231364..8156cc1accb7 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -651,11 +651,11 @@ static void hsw_notification_work(struct work_struct *work) } /* tell DSP that notification has been handled */ - sst_dsp_shim_update_bits_unlocked(hsw->dsp, SST_IPCD, + sst_dsp_shim_update_bits(hsw->dsp, SST_IPCD, SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE); /* unmask busy interrupt */ - sst_dsp_shim_update_bits_unlocked(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0); + sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0); } static struct ipc_message *reply_find_msg(struct sst_hsw *hsw, u32 header) @@ -1228,6 +1228,11 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) struct sst_dsp *sst = hsw->dsp; unsigned long flags; + if (!stream) { + dev_warn(hsw->dev, "warning: stream is NULL, no stream to free, ignore it.\n"); + return 0; + } + /* dont free DSP streams that are not commited */ if (!stream->commited) goto out; @@ -1415,6 +1420,16 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) u32 header; int ret; + if (!stream) { + dev_warn(hsw->dev, "warning: stream is NULL, no stream to commit, ignore it.\n"); + return 0; + } + + if (stream->commited) { + dev_warn(hsw->dev, "warning: stream is already committed, ignore it.\n"); + return 0; + } + trace_ipc_request("stream alloc", stream->host_id); header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM); @@ -1519,6 +1534,11 @@ int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream, { int ret; + if (!stream) { + dev_warn(hsw->dev, "warning: stream is NULL, no stream to pause, ignore it.\n"); + return 0; + } + trace_ipc_request("stream pause", stream->reply.stream_hw_id); ret = sst_hsw_stream_operations(hsw, IPC_STR_PAUSE, @@ -1535,6 +1555,11 @@ int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, { int ret; + if (!stream) { + dev_warn(hsw->dev, "warning: stream is NULL, no stream to resume, ignore it.\n"); + return 0; + } + trace_ipc_request("stream resume", stream->reply.stream_hw_id); ret = sst_hsw_stream_operations(hsw, IPC_STR_RESUME, @@ -1550,6 +1575,11 @@ int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream) { int ret, tries = 10; + if (!stream) { + dev_warn(hsw->dev, "warning: stream is NULL, no stream to reset, ignore it.\n"); + return 0; + } + /* dont reset streams that are not commited */ if (!stream->commited) return 0; diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h index 7f4bbfcbc6f5..562bc483d6b7 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/sst/sst.h @@ -58,6 +58,7 @@ enum sst_algo_ops { #define SST_BLOCK_TIMEOUT 1000 #define FW_SIGNATURE_SIZE 4 +#define FW_NAME_SIZE 32 /* stream states */ enum sst_stream_states { @@ -426,7 +427,7 @@ struct intel_sst_drv { * Holder for firmware name. Due to async call it needs to be * persistent till worker thread gets called */ - char firmware_name[20]; + char firmware_name[FW_NAME_SIZE]; }; /* misc definitions */ diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c index 3abc29e8a928..e541d0e69ea2 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/sst/sst_acpi.c @@ -47,7 +47,7 @@ struct sst_machines { char board[32]; char machine[32]; void (*machine_quirk)(void); - char firmware[32]; + char firmware[FW_NAME_SIZE]; struct sst_platform_info *pdata; }; @@ -245,7 +245,7 @@ static struct sst_machines *sst_acpi_find_machine( return NULL; } -int sst_acpi_probe(struct platform_device *pdev) +static int sst_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret = 0; @@ -332,7 +332,7 @@ do_sst_cleanup: * This function is called by OS when a device is unloaded * This frees the interrupt etc */ -int sst_acpi_remove(struct platform_device *pdev) +static int sst_acpi_remove(struct platform_device *pdev) { struct intel_sst_drv *ctx; @@ -343,14 +343,14 @@ int sst_acpi_remove(struct platform_device *pdev) } static struct sst_machines sst_acpi_bytcr[] = { - {"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin", + {"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin", &byt_rvp_platform_data }, {}, }; /* Cherryview-based platforms: CherryTrail and Braswell */ static struct sst_machines sst_acpi_chv[] = { - {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "fw_sst_22a8.bin", + {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin", &chv_platform_data }, {}, }; @@ -366,7 +366,6 @@ MODULE_DEVICE_TABLE(acpi, sst_acpi_ids); static struct platform_driver sst_acpi_driver = { .driver = { .name = "intel_sst_acpi", - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(sst_acpi_ids), .pm = &intel_sst_pm, }, diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index 3f9ac7dbdc80..ccfb41c22e53 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -393,7 +393,6 @@ static int omap_hdmi_audio_remove(struct platform_device *pdev) static struct platform_driver hdmi_audio_driver = { .driver = { .name = DRV_NAME, - .owner = THIS_MODULE, }, .probe = omap_hdmi_audio_probe, .remove = omap_hdmi_audio_remove, diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 8b79cafab1e2..c7eb9dd67f60 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -434,7 +434,7 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, case SND_SOC_DAIFMT_CBM_CFS: /* McBSP slave. FS clock as output */ regs->srgr2 |= FSGM; - regs->pcr0 |= FSXM; + regs->pcr0 |= FSXM | FSRM; break; case SND_SOC_DAIFMT_CBM_CFM: /* McBSP slave */ diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index d7d5fb20ea6f..a6d680acd907 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -352,7 +352,6 @@ static int spitz_remove(struct platform_device *pdev) static struct platform_driver spitz_driver = { .driver = { .name = "spitz-audio", - .owner = THIS_MODULE, .pm = &snd_soc_pm_ops, }, .probe = spitz_probe, diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 26ec5117b35c..acb5be53bfb4 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -247,6 +247,10 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val); regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val); + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, + I2S_DMACR_TDL(16)); + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, + I2S_DMACR_RDL(16)); return 0; } @@ -335,6 +339,7 @@ static struct snd_soc_dai_driver rockchip_i2s_dai = { SNDRV_PCM_FMTBIT_S24_LE), }, .ops = &rockchip_i2s_dai_ops, + .symmetric_rates = 1, }; static const struct snd_soc_component_driver rockchip_i2s_component = { @@ -454,11 +459,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev) i2s->playback_dma_data.addr = res->start + I2S_TXDR; i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - i2s->playback_dma_data.maxburst = 16; + i2s->playback_dma_data.maxburst = 4; i2s->capture_dma_data.addr = res->start + I2S_RXDR; i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - i2s->capture_dma_data.maxburst = 16; + i2s->capture_dma_data.maxburst = 4; i2s->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, i2s); diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h index 89a5d8bc6ee7..93f456f518a9 100644 --- a/sound/soc/rockchip/rockchip_i2s.h +++ b/sound/soc/rockchip/rockchip_i2s.h @@ -127,7 +127,7 @@ #define I2S_DMACR_TDE_DISABLE (0 << I2S_DMACR_TDE_SHIFT) #define I2S_DMACR_TDE_ENABLE (1 << I2S_DMACR_TDE_SHIFT) #define I2S_DMACR_TDL_SHIFT 0 -#define I2S_DMACR_TDL(x) ((x - 1) << I2S_DMACR_TDL_SHIFT) +#define I2S_DMACR_TDL(x) ((x) << I2S_DMACR_TDL_SHIFT) #define I2S_DMACR_TDL_MASK (0x1f << I2S_DMACR_TDL_SHIFT) /* diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c index 1e2b61ca8db2..8bf2e2c4bafb 100644 --- a/sound/soc/samsung/arndale_rt5631.c +++ b/sound/soc/samsung/arndale_rt5631.c @@ -135,7 +135,6 @@ MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match); static struct platform_driver arndale_audio_driver = { .driver = { .name = "arndale-audio", - .owner = THIS_MODULE, .pm = &snd_soc_pm_ops, .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match), }, diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index 2e10e9a38376..08d7259bbaab 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -48,15 +48,18 @@ static void soc_ac97_device_release(struct device *dev) } /** - * snd_soc_new_ac97_codec - initailise AC97 device - * @codec: audio codec + * snd_soc_alloc_ac97_codec() - Allocate new a AC'97 device + * @codec: The CODEC for which to create the AC'97 device * - * Initialises AC97 codec resources for use by ad-hoc devices only. + * Allocated a new snd_ac97 device and intializes it, but does not yet register + * it. The caller is responsible to either call device_add(&ac97->dev) to + * register the device, or to call put_device(&ac97->dev) to free the device. + * + * Returns: A snd_ac97 device or a PTR_ERR in case of an error. */ -struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec) +struct snd_ac97 *snd_soc_alloc_ac97_codec(struct snd_soc_codec *codec) { struct snd_ac97 *ac97; - int ret; ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); if (ac97 == NULL) @@ -73,7 +76,28 @@ struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec) codec->component.card->snd_card->number, 0, codec->component.name); - ret = device_register(&ac97->dev); + device_initialize(&ac97->dev); + + return ac97; +} +EXPORT_SYMBOL(snd_soc_alloc_ac97_codec); + +/** + * snd_soc_new_ac97_codec - initailise AC97 device + * @codec: audio codec + * + * Initialises AC97 codec resources for use by ad-hoc devices only. + */ +struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec) +{ + struct snd_ac97 *ac97; + int ret; + + ac97 = snd_soc_alloc_ac97_codec(codec); + if (IS_ERR(ac97)) + return ac97; + + ret = device_add(&ac97->dev); if (ret) { put_device(&ac97->dev); return ERR_PTR(ret); diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 590a82f01d0b..025c38fbe3c0 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -659,7 +659,8 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) rtd->dai_link->stream_name); ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, - 1, 0, &be_pcm); + rtd->dai_link->dpcm_playback, + rtd->dai_link->dpcm_capture, &be_pcm); if (ret < 0) { dev_err(rtd->card->dev, "ASoC: can't create compressed for %s\n", rtd->dai_link->name); @@ -668,8 +669,10 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) rtd->pcm = be_pcm; rtd->fe_compr = 1; - be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; - be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; + if (rtd->dai_link->dpcm_playback) + be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; + else if (rtd->dai_link->dpcm_capture) + be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops)); } else memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 985052b3fbed..9f6641bc1e17 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -191,6 +191,39 @@ static ssize_t pmdown_time_set(struct device *dev, static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set); +static struct attribute *soc_dev_attrs[] = { + &dev_attr_codec_reg.attr, + &dev_attr_pmdown_time.attr, + NULL +}; + +static umode_t soc_dev_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); + + if (attr == &dev_attr_pmdown_time.attr) + return attr->mode; /* always visible */ + return rtd->codec ? attr->mode : 0; /* enabled only with codec */ +} + +static const struct attribute_group soc_dapm_dev_group = { + .attrs = soc_dapm_dev_attrs, + .is_visible = soc_dev_attr_is_visible, +}; + +static const struct attribute_group soc_dev_roup = { + .attrs = soc_dev_attrs, + .is_visible = soc_dev_attr_is_visible, +}; + +static const struct attribute_group *soc_dev_attr_groups[] = { + &soc_dapm_dev_group, + &soc_dev_roup, + NULL +}; + #ifdef CONFIG_DEBUG_FS static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -949,8 +982,6 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) /* unregister the rtd device */ if (rtd->dev_registered) { - device_remove_file(rtd->dev, &dev_attr_pmdown_time); - device_remove_file(rtd->dev, &dev_attr_codec_reg); device_unregister(rtd->dev); rtd->dev_registered = 0; } @@ -1120,6 +1151,7 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd, device_initialize(rtd->dev); rtd->dev->parent = rtd->card->dev; rtd->dev->release = rtd_release; + rtd->dev->groups = soc_dev_attr_groups; dev_set_name(rtd->dev, "%s", name); dev_set_drvdata(rtd->dev, rtd); mutex_init(&rtd->pcm_mutex); @@ -1136,23 +1168,6 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd, return ret; } rtd->dev_registered = 1; - - if (rtd->codec) { - /* add DAPM sysfs entries for this codec */ - ret = snd_soc_dapm_sys_add(rtd->dev); - if (ret < 0) - dev_err(rtd->dev, - "ASoC: failed to add codec dapm sysfs entries: %d\n", - ret); - - /* add codec sysfs entries */ - ret = device_create_file(rtd->dev, &dev_attr_codec_reg); - if (ret < 0) - dev_err(rtd->dev, - "ASoC: failed to add codec sysfs files: %d\n", - ret); - } - return 0; } @@ -1308,11 +1323,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) } #endif - ret = device_create_file(rtd->dev, &dev_attr_pmdown_time); - if (ret < 0) - dev_warn(rtd->dev, "ASoC: failed to add pmdown_time sysfs: %d\n", - ret); - if (cpu_dai->driver->compress_dai) { /*create compress_device"*/ ret = soc_new_compress(rtd, num); @@ -1626,9 +1636,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) } } - if (card->fully_routed) - snd_soc_dapm_auto_nc_pins(card); - snd_soc_dapm_new_widgets(card); ret = snd_card_register(card->snd_card); @@ -2386,8 +2393,8 @@ int snd_soc_unregister_card(struct snd_soc_card *card) card->instantiated = false; snd_soc_dapm_shutdown(card); soc_cleanup_card_resources(card); + dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); } - dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); return 0; } @@ -3230,7 +3237,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) { struct device_node *np = card->dev->of_node; - int num_routes, old_routes; + int num_routes; struct snd_soc_dapm_route *routes; int i, ret; @@ -3248,9 +3255,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } - old_routes = card->num_dapm_routes; - routes = devm_kzalloc(card->dev, - (old_routes + num_routes) * sizeof(*routes), + routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes), GFP_KERNEL); if (!routes) { dev_err(card->dev, @@ -3258,11 +3263,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } - memcpy(routes, card->dapm_routes, old_routes * sizeof(*routes)); - for (i = 0; i < num_routes; i++) { ret = of_property_read_string_index(np, propname, - 2 * i, &routes[old_routes + i].sink); + 2 * i, &routes[i].sink); if (ret) { dev_err(card->dev, "ASoC: Property '%s' index %d could not be read: %d\n", @@ -3270,7 +3273,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } ret = of_property_read_string_index(np, propname, - (2 * i) + 1, &routes[old_routes + i].source); + (2 * i) + 1, &routes[i].source); if (ret) { dev_err(card->dev, "ASoC: Property '%s' index %d could not be read: %d\n", @@ -3279,7 +3282,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } } - card->num_dapm_routes += num_routes; + card->num_dapm_routes = num_routes; card->dapm_routes = routes; return 0; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f6ae254e33ef..b6f88202b8c9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -517,8 +517,8 @@ static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm, { if (!dapm->component) return -EIO; - return snd_soc_component_update_bits_async(dapm->component, reg, - mask, value); + return snd_soc_component_update_bits(dapm->component, reg, + mask, value); } static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm, @@ -2127,15 +2127,10 @@ static ssize_t dapm_widget_show(struct device *dev, static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL); -int snd_soc_dapm_sys_add(struct device *dev) -{ - return device_create_file(dev, &dev_attr_dapm_widget); -} - -static void snd_soc_dapm_sys_remove(struct device *dev) -{ - device_remove_file(dev, &dev_attr_dapm_widget); -} +struct attribute *soc_dapm_dev_attrs[] = { + &dev_attr_dapm_widget.attr, + NULL +}; static void dapm_free_path(struct snd_soc_dapm_path *path) { @@ -2279,6 +2274,9 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) switch (w->id) { case snd_soc_dapm_input: + /* On a fully routed card a input is never a source */ + if (w->dapm->card->fully_routed) + break; w->is_source = 1; list_for_each_entry(p, &w->sources, list_sink) { if (p->source->id == snd_soc_dapm_micbias || @@ -2291,6 +2289,9 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) } break; case snd_soc_dapm_output: + /* On a fully routed card a output is never a sink */ + if (w->dapm->card->fully_routed) + break; w->is_sink = 1; list_for_each_entry(p, &w->sinks, list_source) { if (p->sink->id == snd_soc_dapm_spk || @@ -3085,16 +3086,24 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, switch (w->id) { case snd_soc_dapm_mic: - case snd_soc_dapm_input: w->is_source = 1; w->power_check = dapm_generic_check_power; break; + case snd_soc_dapm_input: + if (!dapm->card->fully_routed) + w->is_source = 1; + w->power_check = dapm_generic_check_power; + break; case snd_soc_dapm_spk: case snd_soc_dapm_hp: - case snd_soc_dapm_output: w->is_sink = 1; w->power_check = dapm_generic_check_power; break; + case snd_soc_dapm_output: + if (!dapm->card->fully_routed) + w->is_sink = 1; + w->power_check = dapm_generic_check_power; + break; case snd_soc_dapm_vmid: case snd_soc_dapm_siggen: w->is_source = 1; @@ -3807,93 +3816,6 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); /** - * dapm_is_external_path() - Checks if a path is a external path - * @card: The card the path belongs to - * @path: The path to check - * - * Returns true if the path is either between two different DAPM contexts or - * between two external pins of the same DAPM context. Otherwise returns - * false. - */ -static bool dapm_is_external_path(struct snd_soc_card *card, - struct snd_soc_dapm_path *path) -{ - dev_dbg(card->dev, - "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n", - path->source->name, path->source->id, path->source->dapm, - path->sink->name, path->sink->id, path->sink->dapm); - - /* Connection between two different DAPM contexts */ - if (path->source->dapm != path->sink->dapm) - return true; - - /* Loopback connection from external pin to external pin */ - if (path->sink->id == snd_soc_dapm_input) { - switch (path->source->id) { - case snd_soc_dapm_output: - case snd_soc_dapm_micbias: - return true; - default: - break; - } - } - - return false; -} - -static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card, - struct snd_soc_dapm_widget *w) -{ - struct snd_soc_dapm_path *p; - - list_for_each_entry(p, &w->sources, list_sink) { - if (dapm_is_external_path(card, p)) - return true; - } - - list_for_each_entry(p, &w->sinks, list_source) { - if (dapm_is_external_path(card, p)) - return true; - } - - return false; -} - -/** - * snd_soc_dapm_auto_nc_pins - call snd_soc_dapm_nc_pin for unused pins - * @card: The card whose pins should be processed - * - * Automatically call snd_soc_dapm_nc_pin() for any external pins in the card - * which are unused. Pins are used if they are connected externally to a - * component, whether that be to some other device, or a loop-back connection to - * the component itself. - */ -void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card) -{ - struct snd_soc_dapm_widget *w; - - dev_dbg(card->dev, "ASoC: Auto NC: DAPMs: card:%p\n", &card->dapm); - - list_for_each_entry(w, &card->widgets, list) { - switch (w->id) { - case snd_soc_dapm_input: - case snd_soc_dapm_output: - case snd_soc_dapm_micbias: - dev_dbg(card->dev, "ASoC: Auto NC: Checking widget %s\n", - w->name); - if (!snd_soc_dapm_widget_in_card_paths(card, w)) { - dev_dbg(card->dev, - "... Not in map; disabling\n"); - snd_soc_dapm_nc_pin(w->dapm, w->name); - } - break; - default: - break; - } - } -} - -/** * snd_soc_dapm_free - free dapm resources * @dapm: DAPM context * @@ -3901,7 +3823,6 @@ void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card) */ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) { - snd_soc_dapm_sys_remove(dapm->dev); dapm_debugfs_cleanup(dapm); dapm_free_widgets(dapm); list_del(&dapm->list); diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index b329b84bc5af..4864392bfcba 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -200,11 +200,6 @@ static int dmaengine_pcm_open(struct snd_pcm_substream *substream) return snd_dmaengine_pcm_open(substream, chan); } -static void dmaengine_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - static struct dma_chan *dmaengine_pcm_compat_request_channel( struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream) @@ -283,8 +278,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) if (!pcm->chan[i]) { dev_err(rtd->platform->dev, "Missing dma channel for stream: %d\n", i); - ret = -EINVAL; - goto err_free; + return -EINVAL; } ret = snd_pcm_lib_preallocate_pages(substream, @@ -293,7 +287,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) prealloc_buffer_size, max_buffer_size); if (ret) - goto err_free; + return ret; /* * This will only return false if we know for sure that at least @@ -307,10 +301,6 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) } return 0; - -err_free: - dmaengine_pcm_free(rtd->pcm); - return ret; } static snd_pcm_uframes_t dmaengine_pcm_pointer( @@ -341,7 +331,6 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = { }, .ops = &dmaengine_pcm_ops, .pcm_new = dmaengine_pcm_new, - .pcm_free = dmaengine_pcm_free, }; static const char * const dmaengine_pcm_dma_channel_names[] = { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index eb87d96e2cf0..0ae0e2a9eed7 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -746,7 +746,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) codec_dai); if (ret < 0) { dev_err(codec_dai->dev, - "ASoC: DAI prepare error: %d\n", ret); + "ASoC: codec DAI prepare error: %d\n", + ret); goto out; } } @@ -755,8 +756,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) { ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); if (ret < 0) { - dev_err(cpu_dai->dev, "ASoC: DAI prepare error: %d\n", - ret); + dev_err(cpu_dai->dev, + "ASoC: cpu DAI prepare error: %d\n", ret); goto out; } } diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 272844746135..327f8642ca80 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -816,7 +816,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev) return -EINVAL; } - if (cdev->n_streams < 2) { + if (cdev->n_streams < 1) { dev_err(dev, "bogus number of streams: %d\n", cdev->n_streams); return -EINVAL; } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 41650d5b93b7..3e2ef61c627b 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -913,6 +913,7 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, case USB_ID(0x046d, 0x0807): /* Logitech Webcam C500 */ case USB_ID(0x046d, 0x0808): case USB_ID(0x046d, 0x0809): + case USB_ID(0x046d, 0x0819): /* Logitech Webcam C210 */ case USB_ID(0x046d, 0x081b): /* HD Webcam c310 */ case USB_ID(0x046d, 0x081d): /* HD Webcam c510 */ case USB_ID(0x046d, 0x0825): /* HD Webcam c270 */ |