Merge tag 'sound-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "As shown in diffstat and logs, it was again a busy development cycle at this time, too. The most significant changes are still on-going refactoring / modernization works for ASoC core and drivers, but there are lots of other changes as well. Here we go, some highlights below: ASoC: - Quite a lot of cleanup / refactoring of ASoC core and APIs; most of them are systematic, but also including cleanups and modernization - A bulk of updates for some ASoC platforms, Freescale, sunxi and Intel SST/SOF - Initial support for Sound Open Firmware on i.MX8 - Removal of deprecated w90x900 and nuc900 drivers - New support for Cirrus Logic CS47L15 and CS47L92, Freescale i.MX 7ULP and 8MQ, Meson G12A and NXP UDA1334 USB-audio: - More validations of descriptor units for hardening against bugs reported by fuzzers - PCM device assignment workaround for a past call-order change - Scarlett Gen2 mixer interface, a few more more quirks HD-audio: - Support for audio component with AMD/ATI and Nvidia HDMI codecs - Clean up HD-audio core and remove indirect access ops for Intel SOF - DMIC detection at probe; it would make systems automatically falling back to SST/SOF driver on devices that need DMIC handling. Needs a new Kconfig to set, and beware that it's still new and a bit experimental FireWire: - Lots of code refactoring and cleanups" * tag 'sound-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (521 commits) ASoC: sdm845: remove unneeded semicolon ASoC: fsl_sai: Implement set_bclk_ratio ASoC: dmaengine: Replace strncpy() with strscpy_pad() for pcm->name ASoC: wcd9335: remove redundant use of ret variable ALSA: firewire-tascam: check intermediate state of clock status and retry ALSA: firewire-tascam: handle error code when getting current source of clock ASoC: hdmi-codec: Add an op to set callback function for plug event ASoC: rt5677: keep analog power register at SND_SOC_BIAS_OFF ASoC: rt5677: Remove magic number register writes ASoC: soc-core: self contained soc_unbind_aux_dev() ASoC: soc-core: add soc_unbind_aux_dev() ASoC: soc-core: self contained soc_bind_aux_dev() ASoC: soc-core: move soc_probe_link_dais() next to soc_remove_link_dais() ASoC: soc-core: self contained soc_probe_link_dais() ASoC: soc-core: add new soc_link_init() ASoC: soc-core: move soc_probe_dai() next to soc_remove_dai() ASoC: soc-core: self contained soc_remove_link_dais() ASoC: soc-core: self contained soc_remove_link_components() ASoC: soc-core: self contained soc_probe_link_components() ASoC: rt1308: make array pd static const, makes object smaller ...
Цей коміт міститься в:
@@ -71,8 +71,10 @@ static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value)
|
||||
return 0;
|
||||
}
|
||||
v = i2c_smbus_read_byte_data(onyx->i2c, reg);
|
||||
if (v < 0)
|
||||
if (v < 0) {
|
||||
*value = 0;
|
||||
return -1;
|
||||
}
|
||||
*value = (u8)v;
|
||||
onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value;
|
||||
return 0;
|
||||
|
@@ -2170,7 +2170,7 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
|
||||
|
||||
static const unsigned int rates[] = {
|
||||
5512, 8000, 11025, 16000, 22050, 32000, 44100,
|
||||
48000, 64000, 88200, 96000, 176400, 192000
|
||||
48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000
|
||||
};
|
||||
|
||||
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
|
||||
|
@@ -146,19 +146,24 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
const u32 *src;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
@@ -172,19 +177,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
|
||||
}
|
||||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u32 *dst;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
@@ -284,7 +294,7 @@ static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
|
||||
}
|
||||
|
||||
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int frames)
|
||||
unsigned int frames, unsigned int data_block_counter)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int f, port;
|
||||
@@ -293,7 +303,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
for (f = 0; f < frames; f++) {
|
||||
b = (u8 *)&buffer[p->midi_position];
|
||||
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
port = (data_block_counter + f) % 8;
|
||||
if (f < MAX_MIDI_RX_BLOCKS &&
|
||||
midi_ratelimit_per_packet(s, port) &&
|
||||
p->midi[port] != NULL &&
|
||||
@@ -311,16 +321,20 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
}
|
||||
}
|
||||
|
||||
static void read_midi_messages(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int frames, unsigned int data_block_counter)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int f, port;
|
||||
int len;
|
||||
u8 *b;
|
||||
int f;
|
||||
|
||||
for (f = 0; f < frames; f++) {
|
||||
port = (8 - s->ctx_data.tx.first_dbc + s->data_block_counter + f) % 8;
|
||||
unsigned int port = f;
|
||||
|
||||
if (!(s->flags & CIP_UNALIGHED_DBC))
|
||||
port += data_block_counter;
|
||||
port %= 8;
|
||||
b = (u8 *)&buffer[p->midi_position];
|
||||
|
||||
len = b[0] - 0x80;
|
||||
@@ -331,44 +345,61 @@ static void read_midi_messages(struct amdtp_stream *s,
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks, unsigned int *syt)
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks * p->frame_multiplier;
|
||||
} else {
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks * p->frame_multiplier;
|
||||
} else {
|
||||
write_pcm_silence(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
if (p->midi_ports) {
|
||||
write_midi_messages(s, buf, data_blocks,
|
||||
desc->data_block_counter);
|
||||
}
|
||||
}
|
||||
|
||||
if (p->midi_ports)
|
||||
write_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks, unsigned int *syt)
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks * p->frame_multiplier;
|
||||
} else {
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks * p->frame_multiplier;
|
||||
}
|
||||
|
||||
if (p->midi_ports) {
|
||||
read_midi_messages(s, buf, data_blocks,
|
||||
desc->data_block_counter);
|
||||
}
|
||||
}
|
||||
|
||||
if (p->midi_ports)
|
||||
read_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
@@ -383,15 +414,14 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffe
|
||||
int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM)
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
else
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
|
||||
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
|
||||
process_data_blocks,
|
||||
sizeof(struct amdtp_am824));
|
||||
process_ctx_payloads, sizeof(struct amdtp_am824));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_init);
|
||||
|
@@ -14,8 +14,8 @@
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(amdtp_packet,
|
||||
TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int index),
|
||||
TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, index),
|
||||
TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int index),
|
||||
TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, index),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, second)
|
||||
__field(unsigned int, cycle)
|
||||
@@ -47,7 +47,7 @@ TRACE_EVENT(amdtp_packet,
|
||||
}
|
||||
__entry->payload_quadlets = payload_length / sizeof(__be32);
|
||||
__entry->data_blocks = data_blocks;
|
||||
__entry->data_block_counter = s->data_block_counter,
|
||||
__entry->data_block_counter = data_block_counter,
|
||||
__entry->packet_index = s->packet_index;
|
||||
__entry->irq = !!in_interrupt();
|
||||
__entry->index = index;
|
||||
|
@@ -74,16 +74,16 @@ static void pcm_period_tasklet(unsigned long data);
|
||||
* @dir: the direction of stream
|
||||
* @flags: the packet transmission method to use
|
||||
* @fmt: the value of fmt field in CIP header
|
||||
* @process_data_blocks: callback handler to process data blocks
|
||||
* @process_ctx_payloads: callback handler to process payloads of isoc context
|
||||
* @protocol_size: the size to allocate newly for protocol
|
||||
*/
|
||||
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags,
|
||||
unsigned int fmt,
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks,
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
|
||||
unsigned int protocol_size)
|
||||
{
|
||||
if (process_data_blocks == NULL)
|
||||
if (process_ctx_payloads == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
s->protocol = kzalloc(protocol_size, GFP_KERNEL);
|
||||
@@ -102,7 +102,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
s->callbacked = false;
|
||||
|
||||
s->fmt = fmt;
|
||||
s->process_data_blocks = process_data_blocks;
|
||||
s->process_ctx_payloads = process_ctx_payloads;
|
||||
|
||||
if (dir == AMDTP_OUT_STREAM)
|
||||
s->ctx_data.rx.syt_override = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -473,12 +476,12 @@ static inline int queue_in_packet(struct amdtp_stream *s,
|
||||
}
|
||||
|
||||
static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
|
||||
unsigned int syt)
|
||||
unsigned int data_block_counter, unsigned int syt)
|
||||
{
|
||||
cip_header[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) |
|
||||
(s->data_block_quadlets << CIP_DBS_SHIFT) |
|
||||
((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
|
||||
s->data_block_counter);
|
||||
data_block_counter);
|
||||
cip_header[1] = cpu_to_be32(CIP_EOH |
|
||||
((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
|
||||
((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
|
||||
@@ -487,8 +490,9 @@ static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
|
||||
|
||||
static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
|
||||
struct fw_iso_packet *params,
|
||||
unsigned int data_blocks, unsigned int syt,
|
||||
unsigned int index)
|
||||
unsigned int data_blocks,
|
||||
unsigned int data_block_counter,
|
||||
unsigned int syt, unsigned int index)
|
||||
{
|
||||
unsigned int payload_length;
|
||||
__be32 *cip_header;
|
||||
@@ -496,14 +500,9 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
|
||||
payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets;
|
||||
params->payload_length = payload_length;
|
||||
|
||||
if (s->flags & CIP_DBC_IS_END_EVENT) {
|
||||
s->data_block_counter =
|
||||
(s->data_block_counter + data_blocks) & 0xff;
|
||||
}
|
||||
|
||||
if (!(s->flags & CIP_NO_HEADER)) {
|
||||
cip_header = (__be32 *)params->header;
|
||||
generate_cip_header(s, cip_header, syt);
|
||||
generate_cip_header(s, cip_header, data_block_counter, syt);
|
||||
params->header_length = 2 * sizeof(__be32);
|
||||
payload_length += params->header_length;
|
||||
} else {
|
||||
@@ -511,23 +510,19 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
|
||||
}
|
||||
|
||||
trace_amdtp_packet(s, cycle, cip_header, payload_length, data_blocks,
|
||||
index);
|
||||
|
||||
if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
|
||||
s->data_block_counter =
|
||||
(s->data_block_counter + data_blocks) & 0xff;
|
||||
}
|
||||
data_block_counter, index);
|
||||
}
|
||||
|
||||
static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
|
||||
unsigned int payload_length,
|
||||
unsigned int *data_blocks, unsigned int *dbc,
|
||||
unsigned int *syt)
|
||||
unsigned int *data_blocks,
|
||||
unsigned int *data_block_counter, unsigned int *syt)
|
||||
{
|
||||
u32 cip_header[2];
|
||||
unsigned int sph;
|
||||
unsigned int fmt;
|
||||
unsigned int fdf;
|
||||
unsigned int dbc;
|
||||
bool lost;
|
||||
|
||||
cip_header[0] = be32_to_cpu(buf[0]);
|
||||
@@ -579,17 +574,16 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
|
||||
}
|
||||
|
||||
/* Check data block counter continuity */
|
||||
*dbc = cip_header[0] & CIP_DBC_MASK;
|
||||
dbc = cip_header[0] & CIP_DBC_MASK;
|
||||
if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
|
||||
s->data_block_counter != UINT_MAX)
|
||||
*dbc = s->data_block_counter;
|
||||
*data_block_counter != UINT_MAX)
|
||||
dbc = *data_block_counter;
|
||||
|
||||
if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
|
||||
*dbc == s->ctx_data.tx.first_dbc) ||
|
||||
s->data_block_counter == UINT_MAX) {
|
||||
if ((dbc == 0x00 && (s->flags & CIP_SKIP_DBC_ZERO_CHECK)) ||
|
||||
*data_block_counter == UINT_MAX) {
|
||||
lost = false;
|
||||
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
|
||||
lost = *dbc != s->data_block_counter;
|
||||
lost = dbc != *data_block_counter;
|
||||
} else {
|
||||
unsigned int dbc_interval;
|
||||
|
||||
@@ -598,16 +592,18 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
|
||||
else
|
||||
dbc_interval = *data_blocks;
|
||||
|
||||
lost = *dbc != ((s->data_block_counter + dbc_interval) & 0xff);
|
||||
lost = dbc != ((*data_block_counter + dbc_interval) & 0xff);
|
||||
}
|
||||
|
||||
if (lost) {
|
||||
dev_err(&s->unit->device,
|
||||
"Detect discontinuity of CIP: %02X %02X\n",
|
||||
s->data_block_counter, *dbc);
|
||||
*data_block_counter, dbc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*data_block_counter = dbc;
|
||||
|
||||
*syt = cip_header[1] & CIP_SYT_MASK;
|
||||
|
||||
return 0;
|
||||
@@ -616,10 +612,10 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
|
||||
static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
|
||||
const __be32 *ctx_header,
|
||||
unsigned int *payload_length,
|
||||
unsigned int *data_blocks, unsigned int *syt,
|
||||
unsigned int index)
|
||||
unsigned int *data_blocks,
|
||||
unsigned int *data_block_counter,
|
||||
unsigned int *syt, unsigned int index)
|
||||
{
|
||||
unsigned int dbc;
|
||||
const __be32 *cip_header;
|
||||
int err;
|
||||
|
||||
@@ -635,7 +631,7 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
|
||||
if (!(s->flags & CIP_NO_HEADER)) {
|
||||
cip_header = ctx_header + 2;
|
||||
err = check_cip_header(s, cip_header, *payload_length,
|
||||
data_blocks, &dbc, syt);
|
||||
data_blocks, data_block_counter, syt);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
@@ -645,16 +641,12 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
|
||||
s->data_block_quadlets;
|
||||
*syt = 0;
|
||||
|
||||
if (s->data_block_counter != UINT_MAX)
|
||||
dbc = s->data_block_counter;
|
||||
else
|
||||
dbc = 0;
|
||||
if (*data_block_counter == UINT_MAX)
|
||||
*data_block_counter = 0;
|
||||
}
|
||||
|
||||
s->data_block_counter = dbc;
|
||||
|
||||
trace_amdtp_packet(s, cycle, cip_header, *payload_length, *data_blocks,
|
||||
index);
|
||||
*data_block_counter, index);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -686,6 +678,80 @@ static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp)
|
||||
return increment_cycle_count(cycle, QUEUE_LENGTH);
|
||||
}
|
||||
|
||||
static int generate_device_pkt_descs(struct amdtp_stream *s,
|
||||
struct pkt_desc *descs,
|
||||
const __be32 *ctx_header,
|
||||
unsigned int packets)
|
||||
{
|
||||
unsigned int dbc = s->data_block_counter;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
struct pkt_desc *desc = descs + i;
|
||||
unsigned int index = (s->packet_index + i) % QUEUE_LENGTH;
|
||||
unsigned int cycle;
|
||||
unsigned int payload_length;
|
||||
unsigned int data_blocks;
|
||||
unsigned int syt;
|
||||
|
||||
cycle = compute_cycle_count(ctx_header[1]);
|
||||
|
||||
err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length,
|
||||
&data_blocks, &dbc, &syt, i);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
desc->cycle = cycle;
|
||||
desc->syt = syt;
|
||||
desc->data_blocks = data_blocks;
|
||||
desc->data_block_counter = dbc;
|
||||
desc->ctx_payload = s->buffer.packets[index].buffer;
|
||||
|
||||
if (!(s->flags & CIP_DBC_IS_END_EVENT))
|
||||
dbc = (dbc + desc->data_blocks) & 0xff;
|
||||
|
||||
ctx_header +=
|
||||
s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
|
||||
}
|
||||
|
||||
s->data_block_counter = dbc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void generate_ideal_pkt_descs(struct amdtp_stream *s,
|
||||
struct pkt_desc *descs,
|
||||
const __be32 *ctx_header,
|
||||
unsigned int packets)
|
||||
{
|
||||
unsigned int dbc = s->data_block_counter;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
struct pkt_desc *desc = descs + i;
|
||||
unsigned int index = (s->packet_index + i) % QUEUE_LENGTH;
|
||||
|
||||
desc->cycle = compute_it_cycle(*ctx_header);
|
||||
desc->syt = calculate_syt(s, desc->cycle);
|
||||
desc->data_blocks = calculate_data_blocks(s, desc->syt);
|
||||
|
||||
if (s->flags & CIP_DBC_IS_END_EVENT)
|
||||
dbc = (dbc + desc->data_blocks) & 0xff;
|
||||
|
||||
desc->data_block_counter = dbc;
|
||||
|
||||
if (!(s->flags & CIP_DBC_IS_END_EVENT))
|
||||
dbc = (dbc + desc->data_blocks) & 0xff;
|
||||
|
||||
desc->ctx_payload = s->buffer.packets[index].buffer;
|
||||
|
||||
++ctx_header;
|
||||
}
|
||||
|
||||
s->data_block_counter = dbc;
|
||||
}
|
||||
|
||||
static inline void cancel_stream(struct amdtp_stream *s)
|
||||
{
|
||||
s->packet_index = -1;
|
||||
@@ -694,6 +760,19 @@ static inline void cancel_stream(struct amdtp_stream *s)
|
||||
WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
|
||||
}
|
||||
|
||||
static void process_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets)
|
||||
{
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
pcm_frames = s->process_ctx_payloads(s, descs, packets, pcm);
|
||||
if (pcm)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
}
|
||||
|
||||
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
size_t header_length, void *header,
|
||||
void *private_data)
|
||||
@@ -706,38 +785,31 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
if (s->packet_index < 0)
|
||||
return;
|
||||
|
||||
generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets);
|
||||
|
||||
process_ctx_payloads(s, s->pkt_descs, packets);
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
u32 cycle;
|
||||
const struct pkt_desc *desc = s->pkt_descs + i;
|
||||
unsigned int syt;
|
||||
unsigned int data_blocks;
|
||||
__be32 *buffer;
|
||||
unsigned int pcm_frames;
|
||||
struct {
|
||||
struct fw_iso_packet params;
|
||||
__be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)];
|
||||
} template = { {0}, {0} };
|
||||
struct snd_pcm_substream *pcm;
|
||||
|
||||
cycle = compute_it_cycle(*ctx_header);
|
||||
syt = calculate_syt(s, cycle);
|
||||
data_blocks = calculate_data_blocks(s, syt);
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
pcm_frames = s->process_data_blocks(s, buffer, data_blocks,
|
||||
&syt);
|
||||
if (s->ctx_data.rx.syt_override < 0)
|
||||
syt = desc->syt;
|
||||
else
|
||||
syt = s->ctx_data.rx.syt_override;
|
||||
|
||||
build_it_pkt_header(s, cycle, &template.params, data_blocks,
|
||||
build_it_pkt_header(s, desc->cycle, &template.params,
|
||||
desc->data_blocks, desc->data_block_counter,
|
||||
syt, i);
|
||||
|
||||
if (queue_out_packet(s, &template.params) < 0) {
|
||||
cancel_stream(s);
|
||||
return;
|
||||
}
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm && pcm_frames > 0)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
|
||||
++ctx_header;
|
||||
}
|
||||
|
||||
fw_iso_context_queue_flush(s->context);
|
||||
@@ -748,8 +820,10 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
void *private_data)
|
||||
{
|
||||
struct amdtp_stream *s = private_data;
|
||||
unsigned int i, packets;
|
||||
unsigned int packets;
|
||||
__be32 *ctx_header = header;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
if (s->packet_index < 0)
|
||||
return;
|
||||
@@ -757,48 +831,23 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
// The number of packets in buffer.
|
||||
packets = header_length / s->ctx_data.tx.ctx_header_size;
|
||||
|
||||
for (i = 0; i < packets; i++) {
|
||||
u32 cycle;
|
||||
unsigned int payload_length;
|
||||
unsigned int data_blocks;
|
||||
unsigned int syt;
|
||||
__be32 *buffer;
|
||||
unsigned int pcm_frames = 0;
|
||||
struct fw_iso_packet params = {0};
|
||||
struct snd_pcm_substream *pcm;
|
||||
int err;
|
||||
|
||||
cycle = compute_cycle_count(ctx_header[1]);
|
||||
err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length,
|
||||
&data_blocks, &syt, i);
|
||||
if (err < 0 && err != -EAGAIN)
|
||||
break;
|
||||
|
||||
if (err >= 0) {
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
pcm_frames = s->process_data_blocks(s, buffer,
|
||||
data_blocks, &syt);
|
||||
|
||||
if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
|
||||
s->data_block_counter += data_blocks;
|
||||
s->data_block_counter &= 0xff;
|
||||
}
|
||||
err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets);
|
||||
if (err < 0) {
|
||||
if (err != -EAGAIN) {
|
||||
cancel_stream(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (queue_in_packet(s, ¶ms) < 0)
|
||||
break;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm && pcm_frames > 0)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
|
||||
ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
|
||||
} else {
|
||||
process_ctx_payloads(s, s->pkt_descs, packets);
|
||||
}
|
||||
|
||||
/* Queueing error or detecting invalid payload. */
|
||||
if (i < packets) {
|
||||
cancel_stream(s);
|
||||
return;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
struct fw_iso_packet params = {0};
|
||||
|
||||
if (queue_in_packet(s, ¶ms) < 0) {
|
||||
cancel_stream(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fw_iso_context_queue_flush(s->context);
|
||||
@@ -845,7 +894,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
||||
* amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
|
||||
* device can be started.
|
||||
*/
|
||||
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
||||
static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
||||
{
|
||||
static const struct {
|
||||
unsigned int data_block;
|
||||
@@ -932,6 +981,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
||||
else
|
||||
s->tag = TAG_CIP;
|
||||
|
||||
s->pkt_descs = kcalloc(INTERRUPT_INTERVAL, sizeof(*s->pkt_descs),
|
||||
GFP_KERNEL);
|
||||
if (!s->pkt_descs) {
|
||||
err = -ENOMEM;
|
||||
goto err_context;
|
||||
}
|
||||
|
||||
s->packet_index = 0;
|
||||
do {
|
||||
struct fw_iso_packet params;
|
||||
@@ -943,7 +999,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
||||
err = queue_out_packet(s, ¶ms);
|
||||
}
|
||||
if (err < 0)
|
||||
goto err_context;
|
||||
goto err_pkt_descs;
|
||||
} while (s->packet_index > 0);
|
||||
|
||||
/* NOTE: TAG1 matches CIP. This just affects in stream. */
|
||||
@@ -954,12 +1010,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
||||
s->callbacked = false;
|
||||
err = fw_iso_context_start(s->context, -1, 0, tag);
|
||||
if (err < 0)
|
||||
goto err_context;
|
||||
goto err_pkt_descs;
|
||||
|
||||
mutex_unlock(&s->mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
err_pkt_descs:
|
||||
kfree(s->pkt_descs);
|
||||
err_context:
|
||||
fw_iso_context_destroy(s->context);
|
||||
s->context = ERR_PTR(-1);
|
||||
@@ -970,7 +1027,6 @@ err_unlock:
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_start);
|
||||
|
||||
/**
|
||||
* amdtp_stream_pcm_pointer - get the PCM buffer position
|
||||
@@ -1041,7 +1097,7 @@ EXPORT_SYMBOL(amdtp_stream_update);
|
||||
* All PCM and MIDI devices of the stream must be stopped before the stream
|
||||
* itself can be stopped.
|
||||
*/
|
||||
void amdtp_stream_stop(struct amdtp_stream *s)
|
||||
static void amdtp_stream_stop(struct amdtp_stream *s)
|
||||
{
|
||||
mutex_lock(&s->mutex);
|
||||
|
||||
@@ -1055,12 +1111,12 @@ void amdtp_stream_stop(struct amdtp_stream *s)
|
||||
fw_iso_context_destroy(s->context);
|
||||
s->context = ERR_PTR(-1);
|
||||
iso_packets_buffer_destroy(&s->buffer, s->unit);
|
||||
kfree(s->pkt_descs);
|
||||
|
||||
s->callbacked = false;
|
||||
|
||||
mutex_unlock(&s->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_stop);
|
||||
|
||||
/**
|
||||
* amdtp_stream_pcm_abort - abort the running PCM device
|
||||
@@ -1078,3 +1134,92 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s)
|
||||
snd_pcm_stop_xrun(pcm);
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_pcm_abort);
|
||||
|
||||
/**
|
||||
* amdtp_domain_init - initialize an AMDTP domain structure
|
||||
* @d: the AMDTP domain to initialize.
|
||||
*/
|
||||
int amdtp_domain_init(struct amdtp_domain *d)
|
||||
{
|
||||
INIT_LIST_HEAD(&d->streams);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_init);
|
||||
|
||||
/**
|
||||
* amdtp_domain_destroy - destroy an AMDTP domain structure
|
||||
* @d: the AMDTP domain to destroy.
|
||||
*/
|
||||
void amdtp_domain_destroy(struct amdtp_domain *d)
|
||||
{
|
||||
// At present nothing to do.
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_destroy);
|
||||
|
||||
/**
|
||||
* amdtp_domain_add_stream - register isoc context into the domain.
|
||||
* @d: the AMDTP domain.
|
||||
* @s: the AMDTP stream.
|
||||
* @channel: the isochronous channel on the bus.
|
||||
* @speed: firewire speed code.
|
||||
*/
|
||||
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
|
||||
int channel, int speed)
|
||||
{
|
||||
struct amdtp_stream *tmp;
|
||||
|
||||
list_for_each_entry(tmp, &d->streams, list) {
|
||||
if (s == tmp)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
list_add(&s->list, &d->streams);
|
||||
|
||||
s->channel = channel;
|
||||
s->speed = speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
|
||||
|
||||
/**
|
||||
* amdtp_domain_start - start sending packets for isoc context in the domain.
|
||||
* @d: the AMDTP domain.
|
||||
*/
|
||||
int amdtp_domain_start(struct amdtp_domain *d)
|
||||
{
|
||||
struct amdtp_stream *s;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry(s, &d->streams, list) {
|
||||
err = amdtp_stream_start(s, s->channel, s->speed);
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
list_for_each_entry(s, &d->streams, list)
|
||||
amdtp_stream_stop(s);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_start);
|
||||
|
||||
/**
|
||||
* amdtp_domain_stop - stop sending packets for isoc context in the same domain.
|
||||
* @d: the AMDTP domain to which the isoc contexts belong.
|
||||
*/
|
||||
void amdtp_domain_stop(struct amdtp_domain *d)
|
||||
{
|
||||
struct amdtp_stream *s, *next;
|
||||
|
||||
list_for_each_entry_safe(s, next, &d->streams, list) {
|
||||
list_del(&s->list);
|
||||
|
||||
amdtp_stream_stop(s);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_stop);
|
||||
|
@@ -33,6 +33,8 @@
|
||||
* @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
|
||||
* valid EOH.
|
||||
* @CIP_NO_HEADERS: a lack of headers in packets
|
||||
* @CIP_UNALIGHED_DBC: Only for in-stream. The value of dbc is not alighed to
|
||||
* the value of current SYT_INTERVAL; e.g. initial value is not zero.
|
||||
*/
|
||||
enum cip_flags {
|
||||
CIP_NONBLOCKING = 0x00,
|
||||
@@ -45,6 +47,7 @@ enum cip_flags {
|
||||
CIP_JUMBO_PAYLOAD = 0x40,
|
||||
CIP_HEADER_WITHOUT_EOH = 0x80,
|
||||
CIP_NO_HEADER = 0x100,
|
||||
CIP_UNALIGHED_DBC = 0x200,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -91,12 +94,20 @@ enum amdtp_stream_direction {
|
||||
AMDTP_IN_STREAM
|
||||
};
|
||||
|
||||
struct pkt_desc {
|
||||
u32 cycle;
|
||||
u32 syt;
|
||||
unsigned int data_blocks;
|
||||
unsigned int data_block_counter;
|
||||
__be32 *ctx_payload;
|
||||
};
|
||||
|
||||
struct amdtp_stream;
|
||||
typedef unsigned int (*amdtp_stream_process_data_blocks_t)(
|
||||
typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)(
|
||||
struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt);
|
||||
const struct pkt_desc *desc,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm);
|
||||
struct amdtp_stream {
|
||||
struct fw_unit *unit;
|
||||
enum cip_flags flags;
|
||||
@@ -107,6 +118,7 @@ struct amdtp_stream {
|
||||
struct fw_iso_context *context;
|
||||
struct iso_packets_buffer buffer;
|
||||
int packet_index;
|
||||
struct pkt_desc *pkt_descs;
|
||||
int tag;
|
||||
union {
|
||||
struct {
|
||||
@@ -119,8 +131,6 @@ struct amdtp_stream {
|
||||
// Fixed interval of dbc between previos/current
|
||||
// packets.
|
||||
unsigned int dbc_interval;
|
||||
// Indicate the value of dbc field in a first packet.
|
||||
unsigned int first_dbc;
|
||||
} tx;
|
||||
struct {
|
||||
// To calculate CIP data blocks and tstamp.
|
||||
@@ -131,6 +141,7 @@ struct amdtp_stream {
|
||||
|
||||
// To generate CIP header.
|
||||
unsigned int fdf;
|
||||
int syt_override;
|
||||
} rx;
|
||||
} ctx_data;
|
||||
|
||||
@@ -158,13 +169,18 @@ struct amdtp_stream {
|
||||
|
||||
/* For backends to process data blocks. */
|
||||
void *protocol;
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
|
||||
// For domain.
|
||||
int channel;
|
||||
int speed;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags,
|
||||
unsigned int fmt,
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks,
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
|
||||
unsigned int protocol_size);
|
||||
void amdtp_stream_destroy(struct amdtp_stream *s);
|
||||
|
||||
@@ -172,9 +188,7 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
unsigned int data_block_quadlets);
|
||||
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
|
||||
|
||||
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
|
||||
void amdtp_stream_update(struct amdtp_stream *s);
|
||||
void amdtp_stream_stop(struct amdtp_stream *s);
|
||||
|
||||
int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime);
|
||||
@@ -256,4 +270,17 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
|
||||
msecs_to_jiffies(timeout)) > 0;
|
||||
}
|
||||
|
||||
struct amdtp_domain {
|
||||
struct list_head streams;
|
||||
};
|
||||
|
||||
int amdtp_domain_init(struct amdtp_domain *d);
|
||||
void amdtp_domain_destroy(struct amdtp_domain *d);
|
||||
|
||||
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
|
||||
int channel, int speed);
|
||||
|
||||
int amdtp_domain_start(struct amdtp_domain *d);
|
||||
void amdtp_domain_stop(struct amdtp_domain *d);
|
||||
|
||||
#endif
|
||||
|
@@ -115,6 +115,8 @@ struct snd_bebob {
|
||||
|
||||
/* For BeBoB version quirk. */
|
||||
unsigned int version;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
static inline int
|
||||
|
@@ -445,10 +445,9 @@ start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* start amdtp stream */
|
||||
err = amdtp_stream_start(stream,
|
||||
conn->resources.channel,
|
||||
conn->speed);
|
||||
// start amdtp stream.
|
||||
err = amdtp_domain_add_stream(&bebob->domain, stream,
|
||||
conn->resources.channel, conn->speed);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
@@ -523,7 +522,13 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err = amdtp_domain_init(&bebob->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(bebob, &bebob->tx_stream);
|
||||
destroy_stream(bebob, &bebob->rx_stream);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream,
|
||||
@@ -566,9 +571,7 @@ int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (curr_rate != rate) {
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
|
||||
amdtp_domain_stop(&bebob->domain);
|
||||
break_both_connections(bebob);
|
||||
|
||||
cmp_connection_release(&bebob->out_conn);
|
||||
@@ -620,9 +623,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
||||
// packet queueing error or detecting discontinuity
|
||||
if (amdtp_streaming_error(&bebob->rx_stream) ||
|
||||
amdtp_streaming_error(&bebob->tx_stream)) {
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
|
||||
amdtp_domain_stop(&bebob->domain);
|
||||
break_both_connections(bebob);
|
||||
}
|
||||
|
||||
@@ -640,11 +641,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
||||
return err;
|
||||
|
||||
err = start_stream(bebob, &bebob->rx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to run AMDTP master stream:%d\n", err);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = start_stream(bebob, &bebob->tx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&bebob->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
// The firmware customized by M-Audio uses these commands to
|
||||
@@ -660,21 +666,8 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
||||
}
|
||||
|
||||
if (!amdtp_stream_wait_callback(&bebob->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&bebob->tx_stream)) {
|
||||
err = start_stream(bebob, &bebob->tx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to run AMDTP slave stream:%d\n", err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!amdtp_stream_wait_callback(&bebob->tx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&bebob->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
@@ -683,8 +676,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
amdtp_domain_stop(&bebob->domain);
|
||||
break_both_connections(bebob);
|
||||
return err;
|
||||
}
|
||||
@@ -692,9 +684,7 @@ error:
|
||||
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
|
||||
{
|
||||
if (bebob->substreams_counter == 0) {
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
|
||||
amdtp_domain_stop(&bebob->domain);
|
||||
break_both_connections(bebob);
|
||||
|
||||
cmp_connection_release(&bebob->out_conn);
|
||||
@@ -708,6 +698,8 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
|
||||
*/
|
||||
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
|
||||
{
|
||||
amdtp_domain_destroy(&bebob->domain);
|
||||
|
||||
destroy_stream(bebob, &bebob->tx_stream);
|
||||
destroy_stream(bebob, &bebob->rx_stream);
|
||||
}
|
||||
|
@@ -154,14 +154,10 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
||||
for (i = 0; i < params->count; i++) {
|
||||
reg = cpu_to_be32((u32)-1);
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
amdtp_stream_stop(&dice->tx_stream[i]);
|
||||
|
||||
snd_dice_transaction_write_tx(dice,
|
||||
params->size * i + TX_ISOCHRONOUS,
|
||||
®, sizeof(reg));
|
||||
} else {
|
||||
amdtp_stream_stop(&dice->rx_stream[i]);
|
||||
|
||||
snd_dice_transaction_write_rx(dice,
|
||||
params->size * i + RX_ISOCHRONOUS,
|
||||
®, sizeof(reg));
|
||||
@@ -297,10 +293,11 @@ int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate)
|
||||
if (dice->substreams_counter == 0 || curr_rate != rate) {
|
||||
struct reg_params tx_params, rx_params;
|
||||
|
||||
amdtp_domain_stop(&dice->domain);
|
||||
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
|
||||
release_resources(dice);
|
||||
@@ -377,7 +374,8 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
||||
return err;
|
||||
}
|
||||
|
||||
err = amdtp_stream_start(stream, resources->channel, max_speed);
|
||||
err = amdtp_domain_add_stream(&dice->domain, stream,
|
||||
resources->channel, max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@@ -410,6 +408,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
if (amdtp_streaming_error(&dice->tx_stream[i]) ||
|
||||
amdtp_streaming_error(&dice->rx_stream[i])) {
|
||||
amdtp_domain_stop(&dice->domain);
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
break;
|
||||
}
|
||||
@@ -456,6 +455,10 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = amdtp_domain_start(&dice->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if ((i < tx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->tx_stream[i],
|
||||
@@ -471,6 +474,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_domain_stop(&dice->domain);
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
return err;
|
||||
}
|
||||
@@ -485,8 +489,10 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice)
|
||||
struct reg_params tx_params, rx_params;
|
||||
|
||||
if (dice->substreams_counter == 0) {
|
||||
if (get_register_params(dice, &tx_params, &rx_params) >= 0)
|
||||
if (get_register_params(dice, &tx_params, &rx_params) >= 0) {
|
||||
amdtp_domain_stop(&dice->domain);
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
}
|
||||
|
||||
release_resources(dice);
|
||||
}
|
||||
@@ -564,7 +570,15 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice)
|
||||
destroy_stream(dice, AMDTP_OUT_STREAM, i);
|
||||
for (i = 0; i < MAX_STREAMS; i++)
|
||||
destroy_stream(dice, AMDTP_IN_STREAM, i);
|
||||
break;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
err = amdtp_domain_init(&dice->domain);
|
||||
if (err < 0) {
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
destroy_stream(dice, AMDTP_OUT_STREAM, i);
|
||||
destroy_stream(dice, AMDTP_IN_STREAM, i);
|
||||
}
|
||||
}
|
||||
end:
|
||||
@@ -579,6 +593,8 @@ void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
|
||||
destroy_stream(dice, AMDTP_IN_STREAM, i);
|
||||
destroy_stream(dice, AMDTP_OUT_STREAM, i);
|
||||
}
|
||||
|
||||
amdtp_domain_destroy(&dice->domain);
|
||||
}
|
||||
|
||||
void snd_dice_stream_update_duplex(struct snd_dice *dice)
|
||||
@@ -596,6 +612,8 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice)
|
||||
dice->global_enabled = false;
|
||||
|
||||
if (get_register_params(dice, &tx_params, &rx_params) == 0) {
|
||||
amdtp_domain_stop(&dice->domain);
|
||||
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
}
|
||||
|
@@ -112,6 +112,8 @@ struct snd_dice {
|
||||
bool global_enabled;
|
||||
struct completion clock_accepted;
|
||||
unsigned int substreams_counter;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
enum snd_dice_addr_type {
|
||||
|
@@ -143,17 +143,23 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
const u32 *src;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
buffer++;
|
||||
for (i = 0; i < frames; ++i) {
|
||||
@@ -169,17 +175,23 @@ static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u32 *dst;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
buffer++;
|
||||
for (i = 0; i < frames; ++i) {
|
||||
@@ -234,7 +246,7 @@ static inline void midi_use_bytes(struct amdtp_stream *s,
|
||||
}
|
||||
|
||||
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks)
|
||||
unsigned int data_blocks, unsigned int data_block_counter)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
unsigned int f, port;
|
||||
@@ -242,7 +254,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
u8 *b;
|
||||
|
||||
for (f = 0; f < data_blocks; f++) {
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
port = (data_block_counter + f) % 8;
|
||||
b = (u8 *)&buffer[0];
|
||||
|
||||
len = 0;
|
||||
@@ -329,66 +341,74 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
|
||||
WRITE_ONCE(p->midi[port], midi);
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks;
|
||||
} else {
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
}
|
||||
|
||||
read_midi_messages(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
read_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
write_midi_messages(s, buf, data_blocks,
|
||||
desc->data_block_counter);
|
||||
}
|
||||
|
||||
write_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
enum cip_flags flags;
|
||||
|
||||
/* Use different mode between incoming/outgoing. */
|
||||
// Use different mode between incoming/outgoing.
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
flags = CIP_NONBLOCKING;
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
} else {
|
||||
flags = CIP_BLOCKING;
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
}
|
||||
|
||||
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
|
||||
process_data_blocks, sizeof(struct amdtp_dot));
|
||||
process_ctx_payloads, sizeof(struct amdtp_dot));
|
||||
}
|
||||
|
||||
void amdtp_dot_reset(struct amdtp_stream *s)
|
||||
|
@@ -126,9 +126,6 @@ static void finish_session(struct snd_dg00x *dg00x)
|
||||
{
|
||||
__be32 data;
|
||||
|
||||
amdtp_stream_stop(&dg00x->tx_stream);
|
||||
amdtp_stream_stop(&dg00x->rx_stream);
|
||||
|
||||
data = cpu_to_be32(0x00000003);
|
||||
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
|
||||
@@ -218,29 +215,59 @@ static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
}
|
||||
|
||||
static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
|
||||
{
|
||||
struct fw_iso_resources *resources;
|
||||
enum amdtp_stream_direction dir;
|
||||
int err;
|
||||
|
||||
if (s == &dg00x->tx_stream) {
|
||||
resources = &dg00x->tx_resources;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
} else {
|
||||
resources = &dg00x->rx_resources;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
}
|
||||
|
||||
err = fw_iso_resources_init(resources, dg00x->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_dot_init(s, dg00x->unit, dir);
|
||||
if (err < 0)
|
||||
fw_iso_resources_destroy(resources);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
|
||||
{
|
||||
amdtp_stream_destroy(s);
|
||||
|
||||
if (s == &dg00x->tx_stream)
|
||||
fw_iso_resources_destroy(&dg00x->tx_resources);
|
||||
else
|
||||
fw_iso_resources_destroy(&dg00x->rx_resources);
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* For out-stream. */
|
||||
err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
|
||||
err = init_stream(dg00x, &dg00x->rx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
return err;
|
||||
|
||||
/* For in-stream. */
|
||||
err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
|
||||
err = init_stream(dg00x, &dg00x->tx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
destroy_stream(dg00x, &dg00x->rx_stream);
|
||||
|
||||
err = amdtp_domain_init(&dg00x->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(dg00x, &dg00x->rx_stream);
|
||||
destroy_stream(dg00x, &dg00x->tx_stream);
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_dg00x_stream_destroy_duplex(dg00x);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -250,11 +277,10 @@ error:
|
||||
*/
|
||||
void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
amdtp_stream_destroy(&dg00x->rx_stream);
|
||||
fw_iso_resources_destroy(&dg00x->rx_resources);
|
||||
amdtp_domain_destroy(&dg00x->domain);
|
||||
|
||||
amdtp_stream_destroy(&dg00x->tx_stream);
|
||||
fw_iso_resources_destroy(&dg00x->tx_resources);
|
||||
destroy_stream(dg00x, &dg00x->rx_stream);
|
||||
destroy_stream(dg00x, &dg00x->tx_stream);
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
|
||||
@@ -269,6 +295,8 @@ int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
|
||||
rate = curr_rate;
|
||||
|
||||
if (dg00x->substreams_counter == 0 || curr_rate != rate) {
|
||||
amdtp_domain_stop(&dg00x->domain);
|
||||
|
||||
finish_session(dg00x);
|
||||
|
||||
fw_iso_resources_free(&dg00x->tx_resources);
|
||||
@@ -301,8 +329,10 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
|
||||
return 0;
|
||||
|
||||
if (amdtp_streaming_error(&dg00x->tx_stream) ||
|
||||
amdtp_streaming_error(&dg00x->rx_stream))
|
||||
amdtp_streaming_error(&dg00x->rx_stream)) {
|
||||
amdtp_domain_stop(&dg00x->domain);
|
||||
finish_session(dg00x);
|
||||
}
|
||||
|
||||
if (generation != fw_parent_device(dg00x->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&dg00x->tx_resources);
|
||||
@@ -319,36 +349,30 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
|
||||
* which source of clock is used.
|
||||
*/
|
||||
if (!amdtp_stream_running(&dg00x->rx_stream)) {
|
||||
int spd = fw_parent_device(dg00x->unit)->max_speed;
|
||||
|
||||
err = begin_session(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_stream_start(&dg00x->rx_stream,
|
||||
dg00x->rx_resources.channel,
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
|
||||
dg00x->rx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
|
||||
dg00x->tx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&dg00x->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The value of SYT field in transmitted packets is always 0x0000. Thus,
|
||||
* duplex streams with timestamp synchronization cannot be built.
|
||||
*/
|
||||
if (!amdtp_stream_running(&dg00x->tx_stream)) {
|
||||
err = amdtp_stream_start(&dg00x->tx_stream,
|
||||
dg00x->tx_resources.channel,
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&dg00x->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
@@ -356,6 +380,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_domain_stop(&dg00x->domain);
|
||||
finish_session(dg00x);
|
||||
|
||||
return err;
|
||||
@@ -364,6 +389,7 @@ error:
|
||||
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
if (dg00x->substreams_counter == 0) {
|
||||
amdtp_domain_stop(&dg00x->domain);
|
||||
finish_session(dg00x);
|
||||
|
||||
fw_iso_resources_free(&dg00x->tx_resources);
|
||||
|
@@ -59,6 +59,8 @@ struct snd_dg00x {
|
||||
|
||||
/* Console models have additional MIDI ports for control surface. */
|
||||
bool is_console;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
#define DG00X_ADDR_BASE 0xffffe0000000ull
|
||||
|
@@ -27,19 +27,24 @@ int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
return amdtp_stream_set_parameters(s, rate, data_channels);
|
||||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__le32 *buffer, unsigned int frames)
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__le32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_ff *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
const u32 *src;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
@@ -52,19 +57,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
|
||||
}
|
||||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__le32 *buffer, unsigned int frames)
|
||||
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__le32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_ff *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u32 *dst;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
@@ -102,38 +112,47 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
|
||||
pcm_frames = data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, (__le32 *)buffer, data_blocks);
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__le32 *buf = (__le32 *)desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buf, data_blocks);
|
||||
}
|
||||
}
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
|
||||
pcm_frames = data_blocks;
|
||||
} else {
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__le32 *buf = (__le32 *)desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
}
|
||||
}
|
||||
|
||||
return pcm_frames;
|
||||
@@ -142,13 +161,13 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM)
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
else
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
|
||||
return amdtp_stream_init(s, unit, dir, CIP_NO_HEADER, 0,
|
||||
process_data_blocks, sizeof(struct amdtp_ff));
|
||||
process_ctx_payloads, sizeof(struct amdtp_ff));
|
||||
}
|
||||
|
@@ -32,61 +32,65 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
|
||||
|
||||
static inline void finish_session(struct snd_ff *ff)
|
||||
{
|
||||
amdtp_stream_stop(&ff->tx_stream);
|
||||
amdtp_stream_stop(&ff->rx_stream);
|
||||
|
||||
ff->spec->protocol->finish_session(ff);
|
||||
ff->spec->protocol->switch_fetching_mode(ff, false);
|
||||
}
|
||||
|
||||
static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
|
||||
static int init_stream(struct snd_ff *ff, struct amdtp_stream *s)
|
||||
{
|
||||
int err;
|
||||
struct fw_iso_resources *resources;
|
||||
struct amdtp_stream *stream;
|
||||
enum amdtp_stream_direction dir;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
if (s == &ff->tx_stream) {
|
||||
resources = &ff->tx_resources;
|
||||
stream = &ff->tx_stream;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
} else {
|
||||
resources = &ff->rx_resources;
|
||||
stream = &ff->rx_stream;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
}
|
||||
|
||||
err = fw_iso_resources_init(resources, ff->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_ff_init(stream, ff->unit, dir);
|
||||
err = amdtp_ff_init(s, ff->unit, dir);
|
||||
if (err < 0)
|
||||
fw_iso_resources_destroy(resources);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
|
||||
static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s)
|
||||
{
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
amdtp_stream_destroy(&ff->tx_stream);
|
||||
amdtp_stream_destroy(s);
|
||||
|
||||
if (s == &ff->tx_stream)
|
||||
fw_iso_resources_destroy(&ff->tx_resources);
|
||||
} else {
|
||||
amdtp_stream_destroy(&ff->rx_stream);
|
||||
else
|
||||
fw_iso_resources_destroy(&ff->rx_resources);
|
||||
}
|
||||
}
|
||||
|
||||
int snd_ff_stream_init_duplex(struct snd_ff *ff)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = init_stream(ff, AMDTP_OUT_STREAM);
|
||||
err = init_stream(ff, &ff->rx_stream);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
err = init_stream(ff, &ff->tx_stream);
|
||||
if (err < 0) {
|
||||
destroy_stream(ff, &ff->rx_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = amdtp_domain_init(&ff->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(ff, &ff->rx_stream);
|
||||
destroy_stream(ff, &ff->tx_stream);
|
||||
}
|
||||
|
||||
err = init_stream(ff, AMDTP_IN_STREAM);
|
||||
if (err < 0)
|
||||
destroy_stream(ff, AMDTP_OUT_STREAM);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -96,8 +100,10 @@ end:
|
||||
*/
|
||||
void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
|
||||
{
|
||||
destroy_stream(ff, AMDTP_IN_STREAM);
|
||||
destroy_stream(ff, AMDTP_OUT_STREAM);
|
||||
amdtp_domain_destroy(&ff->domain);
|
||||
|
||||
destroy_stream(ff, &ff->rx_stream);
|
||||
destroy_stream(ff, &ff->tx_stream);
|
||||
}
|
||||
|
||||
int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
|
||||
@@ -114,6 +120,7 @@ int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
|
||||
enum snd_ff_stream_mode mode;
|
||||
int i;
|
||||
|
||||
amdtp_domain_stop(&ff->domain);
|
||||
finish_session(ff);
|
||||
|
||||
fw_iso_resources_free(&ff->tx_resources);
|
||||
@@ -156,25 +163,39 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
||||
return 0;
|
||||
|
||||
if (amdtp_streaming_error(&ff->tx_stream) ||
|
||||
amdtp_streaming_error(&ff->rx_stream))
|
||||
amdtp_streaming_error(&ff->rx_stream)) {
|
||||
amdtp_domain_stop(&ff->domain);
|
||||
finish_session(ff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Regardless of current source of clock signal, drivers transfer some
|
||||
* packets. Then, the device transfers packets.
|
||||
*/
|
||||
if (!amdtp_stream_running(&ff->rx_stream)) {
|
||||
int spd = fw_parent_device(ff->unit)->max_speed;
|
||||
|
||||
err = ff->spec->protocol->begin_session(ff, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_stream_start(&ff->rx_stream,
|
||||
ff->rx_resources.channel,
|
||||
fw_parent_device(ff->unit)->max_speed);
|
||||
err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
|
||||
ff->rx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
|
||||
ff->tx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&ff->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&ff->rx_stream,
|
||||
CALLBACK_TIMEOUT_MS) ||
|
||||
!amdtp_stream_wait_callback(&ff->tx_stream,
|
||||
CALLBACK_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
@@ -185,22 +206,9 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&ff->tx_stream)) {
|
||||
err = amdtp_stream_start(&ff->tx_stream,
|
||||
ff->tx_resources.channel,
|
||||
fw_parent_device(ff->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&ff->tx_stream,
|
||||
CALLBACK_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_domain_stop(&ff->domain);
|
||||
finish_session(ff);
|
||||
|
||||
return err;
|
||||
@@ -209,6 +217,7 @@ error:
|
||||
void snd_ff_stream_stop_duplex(struct snd_ff *ff)
|
||||
{
|
||||
if (ff->substreams_counter == 0) {
|
||||
amdtp_domain_stop(&ff->domain);
|
||||
finish_session(ff);
|
||||
|
||||
fw_iso_resources_free(&ff->tx_resources);
|
||||
@@ -218,12 +227,11 @@ void snd_ff_stream_stop_duplex(struct snd_ff *ff)
|
||||
|
||||
void snd_ff_stream_update_duplex(struct snd_ff *ff)
|
||||
{
|
||||
amdtp_domain_stop(&ff->domain);
|
||||
|
||||
// The device discontinue to transfer packets.
|
||||
amdtp_stream_pcm_abort(&ff->tx_stream);
|
||||
amdtp_stream_stop(&ff->tx_stream);
|
||||
|
||||
amdtp_stream_pcm_abort(&ff->rx_stream);
|
||||
amdtp_stream_stop(&ff->rx_stream);
|
||||
}
|
||||
|
||||
void snd_ff_stream_lock_changed(struct snd_ff *ff)
|
||||
|
@@ -91,6 +91,8 @@ struct snd_ff {
|
||||
int dev_lock_count;
|
||||
bool dev_lock_changed;
|
||||
wait_queue_head_t hwdep_wait;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
enum snd_ff_clock_src {
|
||||
|
@@ -107,6 +107,8 @@ struct snd_efw {
|
||||
u8 *resp_buf;
|
||||
u8 *pull_ptr;
|
||||
u8 *push_ptr;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
int snd_efw_transaction_cmd(struct fw_unit *unit,
|
||||
|
@@ -8,8 +8,7 @@
|
||||
|
||||
#define CALLBACK_TIMEOUT 100
|
||||
|
||||
static int
|
||||
init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
{
|
||||
struct cmp_connection *conn;
|
||||
enum cmp_direction c_dir;
|
||||
@@ -28,28 +27,40 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
|
||||
err = cmp_connection_init(conn, efw->unit, c_dir, 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(stream);
|
||||
cmp_connection_destroy(conn);
|
||||
return err;
|
||||
}
|
||||
end:
|
||||
|
||||
if (stream == &efw->tx_stream) {
|
||||
// Fireworks transmits NODATA packets with TAG0.
|
||||
efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
|
||||
// Fireworks has its own meaning for dbc.
|
||||
efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
|
||||
// Fireworks reset dbc at bus reset.
|
||||
efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
|
||||
// But Recent firmwares starts packets with non-zero dbc.
|
||||
// Driver version 5.7.6 installs firmware version 5.7.3.
|
||||
if (efw->is_fireworks3 &&
|
||||
(efw->firmware_version == 0x5070000 ||
|
||||
efw->firmware_version == 0x5070300 ||
|
||||
efw->firmware_version == 0x5080000))
|
||||
efw->tx_stream.flags |= CIP_UNALIGHED_DBC;
|
||||
// AudioFire9 always reports wrong dbs.
|
||||
if (efw->is_af9)
|
||||
efw->tx_stream.flags |= CIP_WRONG_DBS;
|
||||
// Firmware version 5.5 reports fixed interval for dbc.
|
||||
if (efw->firmware_version == 0x5050000)
|
||||
efw->tx_stream.ctx_data.tx.dbc_interval = 8;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
{
|
||||
amdtp_stream_stop(stream);
|
||||
|
||||
if (stream == &efw->tx_stream)
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
else
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
}
|
||||
|
||||
static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
|
||||
unsigned int rate)
|
||||
{
|
||||
@@ -67,38 +78,26 @@ static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
|
||||
return err;
|
||||
|
||||
// Start amdtp stream.
|
||||
err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
|
||||
err = amdtp_domain_add_stream(&efw->domain, stream,
|
||||
conn->resources.channel, conn->speed);
|
||||
if (err < 0) {
|
||||
cmp_connection_break(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Wait first callback.
|
||||
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(stream);
|
||||
cmp_connection_break(conn);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should be called before starting the stream or after stopping
|
||||
* the streams.
|
||||
*/
|
||||
static void
|
||||
destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
// This function should be called before starting the stream or after stopping
|
||||
// the streams.
|
||||
static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
{
|
||||
struct cmp_connection *conn;
|
||||
amdtp_stream_destroy(stream);
|
||||
|
||||
if (stream == &efw->tx_stream)
|
||||
conn = &efw->out_conn;
|
||||
cmp_connection_destroy(&efw->out_conn);
|
||||
else
|
||||
conn = &efw->in_conn;
|
||||
|
||||
amdtp_stream_destroy(stream);
|
||||
cmp_connection_destroy(conn);
|
||||
cmp_connection_destroy(&efw->in_conn);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -131,42 +130,28 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
|
||||
|
||||
err = init_stream(efw, &efw->tx_stream);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
/* Fireworks transmits NODATA packets with TAG0. */
|
||||
efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
|
||||
/* Fireworks has its own meaning for dbc. */
|
||||
efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
|
||||
/* Fireworks reset dbc at bus reset. */
|
||||
efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
|
||||
/*
|
||||
* But Recent firmwares starts packets with non-zero dbc.
|
||||
* Driver version 5.7.6 installs firmware version 5.7.3.
|
||||
*/
|
||||
if (efw->is_fireworks3 &&
|
||||
(efw->firmware_version == 0x5070000 ||
|
||||
efw->firmware_version == 0x5070300 ||
|
||||
efw->firmware_version == 0x5080000))
|
||||
efw->tx_stream.ctx_data.tx.first_dbc = 0x02;
|
||||
/* AudioFire9 always reports wrong dbs. */
|
||||
if (efw->is_af9)
|
||||
efw->tx_stream.flags |= CIP_WRONG_DBS;
|
||||
/* Firmware version 5.5 reports fixed interval for dbc. */
|
||||
if (efw->firmware_version == 0x5050000)
|
||||
efw->tx_stream.ctx_data.tx.dbc_interval = 8;
|
||||
return err;
|
||||
|
||||
err = init_stream(efw, &efw->rx_stream);
|
||||
if (err < 0) {
|
||||
destroy_stream(efw, &efw->tx_stream);
|
||||
goto end;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* set IEC61883 compliant mode (actually not fully compliant...) */
|
||||
err = amdtp_domain_init(&efw->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(efw, &efw->tx_stream);
|
||||
destroy_stream(efw, &efw->rx_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
// set IEC61883 compliant mode (actually not fully compliant...).
|
||||
err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
|
||||
if (err < 0) {
|
||||
destroy_stream(efw, &efw->tx_stream);
|
||||
destroy_stream(efw, &efw->rx_stream);
|
||||
}
|
||||
end:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -214,8 +199,10 @@ int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate) {
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
amdtp_domain_stop(&efw->domain);
|
||||
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
|
||||
cmp_connection_release(&efw->out_conn);
|
||||
cmp_connection_release(&efw->in_conn);
|
||||
@@ -255,47 +242,57 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw)
|
||||
if (efw->substreams_counter == 0)
|
||||
return -EIO;
|
||||
|
||||
if (amdtp_streaming_error(&efw->rx_stream) ||
|
||||
amdtp_streaming_error(&efw->tx_stream)) {
|
||||
amdtp_domain_stop(&efw->domain);
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
}
|
||||
|
||||
err = snd_efw_command_get_sampling_rate(efw, &rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (amdtp_streaming_error(&efw->rx_stream) ||
|
||||
amdtp_streaming_error(&efw->tx_stream)) {
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
}
|
||||
|
||||
/* master should be always running */
|
||||
if (!amdtp_stream_running(&efw->rx_stream)) {
|
||||
err = start_stream(efw, &efw->rx_stream, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&efw->unit->device,
|
||||
"fail to start AMDTP master stream:%d\n", err);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&efw->tx_stream)) {
|
||||
err = start_stream(efw, &efw->tx_stream, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&efw->unit->device,
|
||||
"fail to start AMDTP slave stream:%d\n", err);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&efw->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
// Wait first callback.
|
||||
if (!amdtp_stream_wait_callback(&efw->rx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&efw->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
amdtp_domain_stop(&efw->domain);
|
||||
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
|
||||
{
|
||||
if (efw->substreams_counter == 0) {
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
amdtp_domain_stop(&efw->domain);
|
||||
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
|
||||
cmp_connection_release(&efw->out_conn);
|
||||
cmp_connection_release(&efw->in_conn);
|
||||
@@ -304,18 +301,19 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
|
||||
|
||||
void snd_efw_stream_update_duplex(struct snd_efw *efw)
|
||||
{
|
||||
if (cmp_connection_update(&efw->out_conn) < 0 ||
|
||||
cmp_connection_update(&efw->in_conn) < 0) {
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
} else {
|
||||
amdtp_stream_update(&efw->rx_stream);
|
||||
amdtp_stream_update(&efw->tx_stream);
|
||||
}
|
||||
amdtp_domain_stop(&efw->domain);
|
||||
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
|
||||
amdtp_stream_pcm_abort(&efw->rx_stream);
|
||||
amdtp_stream_pcm_abort(&efw->tx_stream);
|
||||
}
|
||||
|
||||
void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
|
||||
{
|
||||
amdtp_domain_destroy(&efw->domain);
|
||||
|
||||
destroy_stream(efw, &efw->rx_stream);
|
||||
destroy_stream(efw, &efw->tx_stream);
|
||||
}
|
||||
|
@@ -117,19 +117,25 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
__be32 *buffer, unsigned int data_blocks)
|
||||
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int data_blocks,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_motu *p = s->protocol;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int channels = p->pcm_chunks;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u8 *byte;
|
||||
u32 *dst;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_chunks;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < data_blocks; ++i) {
|
||||
byte = (u8 *)buffer + p->pcm_byte_offset;
|
||||
@@ -147,19 +153,25 @@ static void read_pcm_s32(struct amdtp_stream *s,
|
||||
}
|
||||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
__be32 *buffer, unsigned int data_blocks)
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int data_blocks,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_motu *p = s->protocol;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int channels = p->pcm_chunks;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u8 *byte;
|
||||
const u32 *src;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_chunks;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < data_blocks; ++i) {
|
||||
byte = (u8 *)buffer + p->pcm_byte_offset;
|
||||
@@ -298,24 +310,52 @@ static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static void probe_tracepoints_events(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
trace_data_block_sph(s, data_blocks, buf);
|
||||
trace_data_block_message(s, data_blocks, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct amdtp_motu *p = s->protocol;
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
trace_data_block_sph(s, data_blocks, buffer);
|
||||
trace_data_block_message(s, data_blocks, buffer);
|
||||
// For data block processing.
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (p->midi_ports)
|
||||
read_midi_messages(s, buffer, data_blocks);
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
}
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (data_blocks > 0 && pcm)
|
||||
read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
|
||||
if (p->midi_ports)
|
||||
read_midi_messages(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
return data_blocks;
|
||||
// For tracepoints.
|
||||
if (trace_data_block_sph_enabled() ||
|
||||
trace_data_block_message_enabled())
|
||||
probe_tracepoints_events(s, descs, packets);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
|
||||
@@ -360,46 +400,55 @@ static void write_sph(struct amdtp_stream *s, __be32 *buffer,
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct amdtp_motu *p = (struct amdtp_motu *)s->protocol;
|
||||
struct snd_pcm_substream *pcm;
|
||||
struct amdtp_motu *p = s->protocol;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
/* Not used. */
|
||||
*syt = 0xffff;
|
||||
// For data block processing.
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
/* TODO: how to interact control messages between userspace? */
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
if (p->midi_ports)
|
||||
write_midi_messages(s, buffer, data_blocks);
|
||||
if (p->midi_ports)
|
||||
write_midi_messages(s, buf, data_blocks);
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm)
|
||||
write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
|
||||
else
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
// TODO: how to interact control messages between userspace?
|
||||
|
||||
write_sph(s, buffer, data_blocks);
|
||||
write_sph(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
trace_data_block_sph(s, data_blocks, buffer);
|
||||
trace_data_block_message(s, data_blocks, buffer);
|
||||
// For tracepoints.
|
||||
if (trace_data_block_sph_enabled() ||
|
||||
trace_data_block_message_enabled())
|
||||
probe_tracepoints_events(s, descs, packets);
|
||||
|
||||
return data_blocks;
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir,
|
||||
const struct snd_motu_protocol *const protocol)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
int fmt = CIP_FMT_MOTU;
|
||||
int flags = CIP_BLOCKING;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
|
||||
/*
|
||||
* Units of version 3 transmits packets with invalid CIP header
|
||||
@@ -418,17 +467,23 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
CIP_SKIP_DBC_ZERO_CHECK;
|
||||
}
|
||||
} else {
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
flags |= CIP_DBC_IS_END_EVENT;
|
||||
}
|
||||
|
||||
err = amdtp_stream_init(s, unit, dir, flags, fmt, process_data_blocks,
|
||||
err = amdtp_stream_init(s, unit, dir, flags, fmt, process_ctx_payloads,
|
||||
sizeof(struct amdtp_motu));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
s->sph = 1;
|
||||
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
|
||||
|
||||
if (dir == AMDTP_OUT_STREAM) {
|
||||
// Use fixed value for FDF field.
|
||||
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
|
||||
// Not used.
|
||||
s->ctx_data.rx.syt_override = 0xffff;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -92,9 +92,6 @@ static void finish_session(struct snd_motu *motu)
|
||||
if (err < 0)
|
||||
return;
|
||||
|
||||
amdtp_stream_stop(&motu->tx_stream);
|
||||
amdtp_stream_stop(&motu->rx_stream);
|
||||
|
||||
err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
@@ -109,27 +106,6 @@ static void finish_session(struct snd_motu *motu)
|
||||
sizeof(reg));
|
||||
}
|
||||
|
||||
static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
|
||||
{
|
||||
struct fw_iso_resources *resources;
|
||||
int err;
|
||||
|
||||
if (stream == &motu->rx_stream)
|
||||
resources = &motu->rx_resources;
|
||||
else
|
||||
resources = &motu->tx_resources;
|
||||
|
||||
err = amdtp_stream_start(stream, resources->channel,
|
||||
fw_parent_device(motu->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
|
||||
{
|
||||
int err;
|
||||
@@ -169,6 +145,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate)
|
||||
rate = curr_rate;
|
||||
|
||||
if (motu->substreams_counter == 0 || curr_rate != rate) {
|
||||
amdtp_domain_stop(&motu->domain);
|
||||
finish_session(motu);
|
||||
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
@@ -234,8 +211,10 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
||||
return 0;
|
||||
|
||||
if (amdtp_streaming_error(&motu->rx_stream) ||
|
||||
amdtp_streaming_error(&motu->tx_stream))
|
||||
amdtp_streaming_error(&motu->tx_stream)) {
|
||||
amdtp_domain_stop(&motu->domain);
|
||||
finish_session(motu);
|
||||
}
|
||||
|
||||
if (generation != fw_parent_device(motu->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&motu->rx_resources);
|
||||
@@ -248,6 +227,8 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&motu->rx_stream)) {
|
||||
int spd = fw_parent_device(motu->unit)->max_speed;
|
||||
|
||||
err = ensure_packet_formats(motu);
|
||||
if (err < 0)
|
||||
return err;
|
||||
@@ -259,10 +240,25 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
||||
goto stop_streams;
|
||||
}
|
||||
|
||||
err = start_isoc_ctx(motu, &motu->rx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to start IT context: %d\n", err);
|
||||
err = amdtp_domain_add_stream(&motu->domain, &motu->tx_stream,
|
||||
motu->tx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto stop_streams;
|
||||
|
||||
err = amdtp_domain_add_stream(&motu->domain, &motu->rx_stream,
|
||||
motu->rx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto stop_streams;
|
||||
|
||||
err = amdtp_domain_start(&motu->domain);
|
||||
if (err < 0)
|
||||
goto stop_streams;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&motu->tx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&motu->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto stop_streams;
|
||||
}
|
||||
|
||||
@@ -274,18 +270,10 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&motu->tx_stream)) {
|
||||
err = start_isoc_ctx(motu, &motu->tx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to start IR context: %d", err);
|
||||
goto stop_streams;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
stop_streams:
|
||||
amdtp_domain_stop(&motu->domain);
|
||||
finish_session(motu);
|
||||
return err;
|
||||
}
|
||||
@@ -293,6 +281,7 @@ stop_streams:
|
||||
void snd_motu_stream_stop_duplex(struct snd_motu *motu)
|
||||
{
|
||||
if (motu->substreams_counter == 0) {
|
||||
amdtp_domain_stop(&motu->domain);
|
||||
finish_session(motu);
|
||||
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
@@ -300,74 +289,72 @@ void snd_motu_stream_stop_duplex(struct snd_motu *motu)
|
||||
}
|
||||
}
|
||||
|
||||
static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir)
|
||||
static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)
|
||||
{
|
||||
int err;
|
||||
struct amdtp_stream *stream;
|
||||
struct fw_iso_resources *resources;
|
||||
enum amdtp_stream_direction dir;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &motu->tx_stream;
|
||||
if (s == &motu->tx_stream) {
|
||||
resources = &motu->tx_resources;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
} else {
|
||||
stream = &motu->rx_stream;
|
||||
resources = &motu->rx_resources;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
}
|
||||
|
||||
err = fw_iso_resources_init(resources, motu->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(stream);
|
||||
err = amdtp_motu_init(s, motu->unit, dir, motu->spec->protocol);
|
||||
if (err < 0)
|
||||
fw_iso_resources_destroy(resources);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void destroy_stream(struct snd_motu *motu,
|
||||
enum amdtp_stream_direction dir)
|
||||
static void destroy_stream(struct snd_motu *motu, struct amdtp_stream *s)
|
||||
{
|
||||
struct amdtp_stream *stream;
|
||||
struct fw_iso_resources *resources;
|
||||
amdtp_stream_destroy(s);
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &motu->tx_stream;
|
||||
resources = &motu->tx_resources;
|
||||
} else {
|
||||
stream = &motu->rx_stream;
|
||||
resources = &motu->rx_resources;
|
||||
}
|
||||
|
||||
amdtp_stream_destroy(stream);
|
||||
fw_iso_resources_destroy(resources);
|
||||
if (s == &motu->tx_stream)
|
||||
fw_iso_resources_destroy(&motu->tx_resources);
|
||||
else
|
||||
fw_iso_resources_destroy(&motu->rx_resources);
|
||||
}
|
||||
|
||||
int snd_motu_stream_init_duplex(struct snd_motu *motu)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = init_stream(motu, AMDTP_IN_STREAM);
|
||||
err = init_stream(motu, &motu->tx_stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = init_stream(motu, AMDTP_OUT_STREAM);
|
||||
if (err < 0)
|
||||
destroy_stream(motu, AMDTP_IN_STREAM);
|
||||
err = init_stream(motu, &motu->rx_stream);
|
||||
if (err < 0) {
|
||||
destroy_stream(motu, &motu->tx_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = amdtp_domain_init(&motu->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(motu, &motu->tx_stream);
|
||||
destroy_stream(motu, &motu->rx_stream);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should be called before starting streams or after stopping
|
||||
* streams.
|
||||
*/
|
||||
// This function should be called before starting streams or after stopping
|
||||
// streams.
|
||||
void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
|
||||
{
|
||||
destroy_stream(motu, AMDTP_IN_STREAM);
|
||||
destroy_stream(motu, AMDTP_OUT_STREAM);
|
||||
amdtp_domain_destroy(&motu->domain);
|
||||
|
||||
destroy_stream(motu, &motu->rx_stream);
|
||||
destroy_stream(motu, &motu->tx_stream);
|
||||
|
||||
motu->substreams_counter = 0;
|
||||
}
|
||||
|
@@ -247,6 +247,17 @@ static const struct snd_motu_spec motu_audio_express = {
|
||||
.analog_out_ports = 4,
|
||||
};
|
||||
|
||||
static const struct snd_motu_spec motu_4pre = {
|
||||
.name = "4pre",
|
||||
.protocol = &snd_motu_protocol_v3,
|
||||
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
|
||||
SND_MOTU_SPEC_TX_MICINST_CHUNK |
|
||||
SND_MOTU_SPEC_TX_RETURN_CHUNK |
|
||||
SND_MOTU_SPEC_RX_SEPARETED_MAIN,
|
||||
.analog_in_ports = 2,
|
||||
.analog_out_ports = 2,
|
||||
};
|
||||
|
||||
#define SND_MOTU_DEV_ENTRY(model, data) \
|
||||
{ \
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
|
||||
@@ -265,6 +276,7 @@ static const struct ieee1394_device_id motu_id_table[] = {
|
||||
SND_MOTU_DEV_ENTRY(0x000015, &motu_828mk3), /* FireWire only. */
|
||||
SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */
|
||||
SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express),
|
||||
SND_MOTU_DEV_ENTRY(0x000045, &motu_4pre),
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
|
||||
|
@@ -69,6 +69,8 @@ struct snd_motu {
|
||||
int dev_lock_count;
|
||||
bool dev_lock_changed;
|
||||
wait_queue_head_t hwdep_wait;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
enum snd_motu_spec_flags {
|
||||
|
@@ -114,19 +114,13 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
|
||||
err = amdtp_domain_add_stream(&oxfw->domain, stream,
|
||||
conn->resources.channel, conn->speed);
|
||||
if (err < 0) {
|
||||
cmp_connection_break(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Wait first packet.
|
||||
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(stream);
|
||||
cmp_connection_break(conn);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -280,12 +274,12 @@ int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
|
||||
pcm_channels = formation.pcm;
|
||||
}
|
||||
if (formation.rate != rate || formation.pcm != pcm_channels) {
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
amdtp_domain_stop(&oxfw->domain);
|
||||
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
cmp_connection_release(&oxfw->in_conn);
|
||||
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
cmp_connection_release(&oxfw->out_conn);
|
||||
}
|
||||
@@ -325,30 +319,46 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
|
||||
|
||||
if (amdtp_streaming_error(&oxfw->rx_stream) ||
|
||||
amdtp_streaming_error(&oxfw->tx_stream)) {
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
amdtp_domain_stop(&oxfw->domain);
|
||||
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
if (oxfw->has_output)
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&oxfw->rx_stream)) {
|
||||
err = start_stream(oxfw, &oxfw->rx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&oxfw->unit->device,
|
||||
"fail to start rx stream: %d\n", err);
|
||||
"fail to prepare rx stream: %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (oxfw->has_output) {
|
||||
if (!amdtp_stream_running(&oxfw->tx_stream)) {
|
||||
if (oxfw->has_output &&
|
||||
!amdtp_stream_running(&oxfw->tx_stream)) {
|
||||
err = start_stream(oxfw, &oxfw->tx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&oxfw->unit->device,
|
||||
"fail to start tx stream: %d\n", err);
|
||||
"fail to prepare tx stream: %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
err = amdtp_domain_start(&oxfw->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
// Wait first packet.
|
||||
if (!amdtp_stream_wait_callback(&oxfw->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (oxfw->has_output) {
|
||||
if (!amdtp_stream_wait_callback(&oxfw->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
@@ -356,24 +366,24 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
amdtp_domain_stop(&oxfw->domain);
|
||||
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
if (oxfw->has_output)
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
if (oxfw->substreams_count == 0) {
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
amdtp_domain_stop(&oxfw->domain);
|
||||
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
cmp_connection_release(&oxfw->in_conn);
|
||||
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
cmp_connection_release(&oxfw->out_conn);
|
||||
}
|
||||
@@ -409,13 +419,22 @@ int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err = amdtp_domain_init(&oxfw->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(oxfw, &oxfw->rx_stream);
|
||||
if (oxfw->has_output)
|
||||
destroy_stream(oxfw, &oxfw->tx_stream);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// This function should be called before starting the stream or after stopping
|
||||
// the streams.
|
||||
void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
amdtp_domain_destroy(&oxfw->domain);
|
||||
|
||||
destroy_stream(oxfw, &oxfw->rx_stream);
|
||||
|
||||
if (oxfw->has_output)
|
||||
@@ -424,13 +443,13 @@ void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
|
||||
|
||||
void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
amdtp_domain_stop(&oxfw->domain);
|
||||
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
|
||||
amdtp_stream_pcm_abort(&oxfw->rx_stream);
|
||||
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
|
||||
amdtp_stream_pcm_abort(&oxfw->tx_stream);
|
||||
|
@@ -63,6 +63,8 @@ struct snd_oxfw {
|
||||
|
||||
const struct ieee1394_device_id *entry;
|
||||
void *spec;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@@ -32,19 +32,24 @@ int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
|
||||
return amdtp_stream_set_parameters(s, rate, data_channels);
|
||||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_tscm *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
const u32 *src;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
@@ -57,19 +62,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
|
||||
}
|
||||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_tscm *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u32 *dst;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
/* The first data channel is for event counter. */
|
||||
buffer += 1;
|
||||
@@ -165,65 +175,82 @@ static void read_status_messages(struct amdtp_stream *s,
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (data_blocks > 0 && pcm)
|
||||
read_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
read_status_messages(s, buffer, data_blocks);
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
}
|
||||
|
||||
return data_blocks;
|
||||
read_status_messages(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
/* This field is not used. */
|
||||
*syt = 0x0000;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm)
|
||||
write_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
else
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buf, data_blocks);
|
||||
}
|
||||
}
|
||||
|
||||
return data_blocks;
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, unsigned int pcm_channels)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
struct amdtp_tscm *p;
|
||||
unsigned int fmt;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
fmt = AMDTP_FMT_TSCM_TX;
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
} else {
|
||||
fmt = AMDTP_FMT_TSCM_RX;
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
}
|
||||
|
||||
err = amdtp_stream_init(s, unit, dir,
|
||||
CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
|
||||
process_data_blocks, sizeof(struct amdtp_tscm));
|
||||
CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
|
||||
process_ctx_payloads, sizeof(struct amdtp_tscm));
|
||||
if (err < 0)
|
||||
return 0;
|
||||
|
||||
/* Use fixed value for FDF field. */
|
||||
s->ctx_data.rx.fdf = 0x00;
|
||||
if (dir == AMDTP_OUT_STREAM) {
|
||||
// Use fixed value for FDF field.
|
||||
s->ctx_data.rx.fdf = 0x00;
|
||||
// Not used.
|
||||
s->ctx_data.rx.syt_override = 0x0000;
|
||||
}
|
||||
|
||||
/* This protocol uses fixed number of data channels for PCM samples. */
|
||||
p = s->protocol;
|
||||
|
@@ -56,6 +56,9 @@ static int pcm_open(struct snd_pcm_substream *substream)
|
||||
goto err_locked;
|
||||
|
||||
err = snd_tscm_stream_get_clock(tscm, &clock);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
|
||||
if (clock != SND_TSCM_CLOCK_INTERNAL ||
|
||||
amdtp_stream_pcm_running(&tscm->rx_stream) ||
|
||||
amdtp_stream_pcm_running(&tscm->tx_stream)) {
|
||||
|
@@ -8,20 +8,37 @@
|
||||
#include <linux/delay.h>
|
||||
#include "tascam.h"
|
||||
|
||||
#define CLOCK_STATUS_MASK 0xffff0000
|
||||
#define CLOCK_CONFIG_MASK 0x0000ffff
|
||||
|
||||
#define CALLBACK_TIMEOUT 500
|
||||
|
||||
static int get_clock(struct snd_tscm *tscm, u32 *data)
|
||||
{
|
||||
int trial = 0;
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
|
||||
®, sizeof(reg), 0);
|
||||
if (err >= 0)
|
||||
*data = be32_to_cpu(reg);
|
||||
while (trial++ < 5) {
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return err;
|
||||
*data = be32_to_cpu(reg);
|
||||
if (*data & CLOCK_STATUS_MASK)
|
||||
break;
|
||||
|
||||
// In intermediate state after changing clock status.
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
// Still in the intermediate state.
|
||||
if (trial >= 5)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_clock(struct snd_tscm *tscm, unsigned int rate,
|
||||
@@ -34,7 +51,7 @@ static int set_clock(struct snd_tscm *tscm, unsigned int rate,
|
||||
err = get_clock(tscm, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data &= 0x0000ffff;
|
||||
data &= CLOCK_CONFIG_MASK;
|
||||
|
||||
if (rate > 0) {
|
||||
data &= 0x000000ff;
|
||||
@@ -79,17 +96,14 @@ static int set_clock(struct snd_tscm *tscm, unsigned int rate,
|
||||
|
||||
int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
|
||||
{
|
||||
u32 data = 0x0;
|
||||
unsigned int trials = 0;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
while (data == 0x0 || trials++ < 5) {
|
||||
err = get_clock(tscm, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = get_clock(tscm, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = (data & 0xff000000) >> 24;
|
||||
}
|
||||
data = (data & 0xff000000) >> 24;
|
||||
|
||||
/* Check base rate. */
|
||||
if ((data & 0x0f) == 0x01)
|
||||
@@ -180,9 +194,6 @@ static void finish_session(struct snd_tscm *tscm)
|
||||
{
|
||||
__be32 reg;
|
||||
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
|
||||
reg = 0;
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
|
||||
@@ -287,38 +298,68 @@ static int keep_resources(struct snd_tscm *tscm, unsigned int rate,
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
|
||||
static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
|
||||
{
|
||||
struct fw_iso_resources *resources;
|
||||
enum amdtp_stream_direction dir;
|
||||
unsigned int pcm_channels;
|
||||
int err;
|
||||
|
||||
/* For out-stream. */
|
||||
err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm_channels = tscm->spec->pcm_playback_analog_channels;
|
||||
if (s == &tscm->tx_stream) {
|
||||
resources = &tscm->tx_resources;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
pcm_channels = tscm->spec->pcm_capture_analog_channels;
|
||||
} else {
|
||||
resources = &tscm->rx_resources;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
pcm_channels = tscm->spec->pcm_playback_analog_channels;
|
||||
}
|
||||
|
||||
if (tscm->spec->has_adat)
|
||||
pcm_channels += 8;
|
||||
if (tscm->spec->has_spdif)
|
||||
pcm_channels += 2;
|
||||
err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
|
||||
pcm_channels);
|
||||
|
||||
err = fw_iso_resources_init(resources, tscm->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* For in-stream. */
|
||||
err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
|
||||
err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels);
|
||||
if (err < 0)
|
||||
fw_iso_resources_free(resources);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
|
||||
{
|
||||
amdtp_stream_destroy(s);
|
||||
|
||||
if (s == &tscm->tx_stream)
|
||||
fw_iso_resources_destroy(&tscm->tx_resources);
|
||||
else
|
||||
fw_iso_resources_destroy(&tscm->rx_resources);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = init_stream(tscm, &tscm->tx_stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm_channels = tscm->spec->pcm_capture_analog_channels;
|
||||
if (tscm->spec->has_adat)
|
||||
pcm_channels += 8;
|
||||
if (tscm->spec->has_spdif)
|
||||
pcm_channels += 2;
|
||||
err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
|
||||
pcm_channels);
|
||||
if (err < 0)
|
||||
amdtp_stream_destroy(&tscm->rx_stream);
|
||||
|
||||
err = init_stream(tscm, &tscm->rx_stream);
|
||||
if (err < 0) {
|
||||
destroy_stream(tscm, &tscm->tx_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = amdtp_domain_init(&tscm->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(tscm, &tscm->tx_stream);
|
||||
destroy_stream(tscm, &tscm->rx_stream);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -326,24 +367,20 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
|
||||
// At bus reset, streaming is stopped and some registers are clear.
|
||||
void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
amdtp_stream_pcm_abort(&tscm->tx_stream);
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
amdtp_domain_stop(&tscm->domain);
|
||||
|
||||
amdtp_stream_pcm_abort(&tscm->tx_stream);
|
||||
amdtp_stream_pcm_abort(&tscm->rx_stream);
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should be called before starting streams or after stopping
|
||||
* streams.
|
||||
*/
|
||||
// This function should be called before starting streams or after stopping
|
||||
// streams.
|
||||
void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
amdtp_stream_destroy(&tscm->rx_stream);
|
||||
amdtp_stream_destroy(&tscm->tx_stream);
|
||||
amdtp_domain_destroy(&tscm->domain);
|
||||
|
||||
fw_iso_resources_destroy(&tscm->rx_resources);
|
||||
fw_iso_resources_destroy(&tscm->tx_resources);
|
||||
destroy_stream(tscm, &tscm->rx_stream);
|
||||
destroy_stream(tscm, &tscm->tx_stream);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
@@ -356,6 +393,8 @@ int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
return err;
|
||||
|
||||
if (tscm->substreams_counter == 0 || rate != curr_rate) {
|
||||
amdtp_domain_stop(&tscm->domain);
|
||||
|
||||
finish_session(tscm);
|
||||
|
||||
fw_iso_resources_free(&tscm->tx_resources);
|
||||
@@ -388,8 +427,10 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
return 0;
|
||||
|
||||
if (amdtp_streaming_error(&tscm->rx_stream) ||
|
||||
amdtp_streaming_error(&tscm->tx_stream))
|
||||
amdtp_streaming_error(&tscm->tx_stream)) {
|
||||
amdtp_domain_stop(&tscm->domain);
|
||||
finish_session(tscm);
|
||||
}
|
||||
|
||||
if (generation != fw_parent_device(tscm->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&tscm->tx_resources);
|
||||
@@ -402,6 +443,8 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->rx_stream)) {
|
||||
int spd = fw_parent_device(tscm->unit)->max_speed;
|
||||
|
||||
err = set_stream_formats(tscm, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
@@ -410,27 +453,23 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_stream_start(&tscm->rx_stream,
|
||||
tscm->rx_resources.channel,
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream,
|
||||
tscm->rx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream,
|
||||
tscm->tx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&tscm->domain);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&tscm->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->tx_stream)) {
|
||||
err = amdtp_stream_start(&tscm->tx_stream,
|
||||
tscm->tx_resources.channel,
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&tscm->tx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&tscm->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
@@ -439,6 +478,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_domain_stop(&tscm->domain);
|
||||
finish_session(tscm);
|
||||
|
||||
return err;
|
||||
@@ -447,6 +487,7 @@ error:
|
||||
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
if (tscm->substreams_counter == 0) {
|
||||
amdtp_domain_stop(&tscm->domain);
|
||||
finish_session(tscm);
|
||||
|
||||
fw_iso_resources_free(&tscm->tx_resources);
|
||||
|
@@ -39,6 +39,9 @@ static const struct snd_tscm_spec model_specs[] = {
|
||||
.midi_capture_ports = 2,
|
||||
.midi_playback_ports = 4,
|
||||
},
|
||||
// This kernel module doesn't support FE-8 because the most of features
|
||||
// can be implemented in userspace without any specific support of this
|
||||
// module.
|
||||
};
|
||||
|
||||
static int identify_model(struct snd_tscm *tscm)
|
||||
@@ -214,7 +217,6 @@ static const struct ieee1394_device_id snd_tscm_id_table[] = {
|
||||
.vendor_id = 0x00022e,
|
||||
.specifier_id = 0x00022e,
|
||||
},
|
||||
/* FE-08 requires reverse-engineering because it just has faders. */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
|
||||
|
@@ -97,6 +97,8 @@ struct snd_tscm {
|
||||
struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT];
|
||||
unsigned int pull_pos;
|
||||
unsigned int push_pos;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
#define TSCM_ADDR_BASE 0xffff00000000ull
|
||||
@@ -127,6 +129,26 @@ struct snd_tscm {
|
||||
|
||||
#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000
|
||||
|
||||
// Although FE-8 supports the above registers, it has no I/O interfaces for
|
||||
// audio samples and music messages. Otherwise it supports another notification
|
||||
// for status and control message as well as LED brightening. The message
|
||||
// consists of quadlet-aligned data up to 32 quadlets. The first byte of message
|
||||
// is fixed to 0x40. The second byte is between 0x00 to 0x1f and represent each
|
||||
// control:
|
||||
// fader: 0x00-0x07
|
||||
// button: 0x0d, 0x0e
|
||||
// knob: 0x14-0x1b
|
||||
// sensing: 0x0b
|
||||
//
|
||||
// The rest two bytes represent state of the controls; e.g. current value for
|
||||
// fader and knob, bitmasks for button and sensing.
|
||||
// Just after turning on, 32 quadlets messages with 0x00-0x1f are immediately
|
||||
// sent in one transaction. After, several quadlets are sent in one transaction.
|
||||
//
|
||||
// TSCM_OFFSET_FE8_CTL_TX_ON 0x0310
|
||||
// TSCM_OFFSET_FE8_CTL_TX_ADDR_HI 0x0314
|
||||
// TSCM_OFFSET_FE8_CTL_TX_ADDR_LO 0x0318
|
||||
|
||||
enum snd_tscm_clock {
|
||||
SND_TSCM_CLOCK_INTERNAL = 0,
|
||||
SND_TSCM_CLOCK_WORD = 1,
|
||||
|
@@ -6,6 +6,9 @@ config SND_HDA_CORE
|
||||
config SND_HDA_DSP_LOADER
|
||||
bool
|
||||
|
||||
config SND_HDA_ALIGNED_MMIO
|
||||
bool
|
||||
|
||||
config SND_HDA_COMPONENT
|
||||
bool
|
||||
|
||||
@@ -29,3 +32,8 @@ config SND_HDA_PREALLOC_SIZE
|
||||
|
||||
Note that the pre-allocation size can be changed dynamically
|
||||
via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
|
||||
|
||||
config SND_INTEL_NHLT
|
||||
tristate
|
||||
# this config should be selected only for Intel ACPI platforms.
|
||||
# A fallback is provided so that the code compiles in all cases.
|
@@ -13,3 +13,6 @@ obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
|
||||
|
||||
#extended hda
|
||||
obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
|
||||
|
||||
snd-intel-nhlt-objs := intel-nhlt.o
|
||||
obj-$(CONFIG_SND_INTEL_NHLT) += snd-intel-nhlt.o
|
||||
|
@@ -17,80 +17,22 @@
|
||||
MODULE_DESCRIPTION("HDA extended core");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static void hdac_ext_writel(u32 value, u32 __iomem *addr)
|
||||
{
|
||||
writel(value, addr);
|
||||
}
|
||||
|
||||
static u32 hdac_ext_readl(u32 __iomem *addr)
|
||||
{
|
||||
return readl(addr);
|
||||
}
|
||||
|
||||
static void hdac_ext_writew(u16 value, u16 __iomem *addr)
|
||||
{
|
||||
writew(value, addr);
|
||||
}
|
||||
|
||||
static u16 hdac_ext_readw(u16 __iomem *addr)
|
||||
{
|
||||
return readw(addr);
|
||||
}
|
||||
|
||||
static void hdac_ext_writeb(u8 value, u8 __iomem *addr)
|
||||
{
|
||||
writeb(value, addr);
|
||||
}
|
||||
|
||||
static u8 hdac_ext_readb(u8 __iomem *addr)
|
||||
{
|
||||
return readb(addr);
|
||||
}
|
||||
|
||||
static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type,
|
||||
size_t size, struct snd_dma_buffer *buf)
|
||||
{
|
||||
return snd_dma_alloc_pages(type, bus->dev, size, buf);
|
||||
}
|
||||
|
||||
static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
|
||||
{
|
||||
snd_dma_free_pages(buf);
|
||||
}
|
||||
|
||||
static const struct hdac_io_ops hdac_ext_default_io = {
|
||||
.reg_writel = hdac_ext_writel,
|
||||
.reg_readl = hdac_ext_readl,
|
||||
.reg_writew = hdac_ext_writew,
|
||||
.reg_readw = hdac_ext_readw,
|
||||
.reg_writeb = hdac_ext_writeb,
|
||||
.reg_readb = hdac_ext_readb,
|
||||
.dma_alloc_pages = hdac_ext_dma_alloc_pages,
|
||||
.dma_free_pages = hdac_ext_dma_free_pages,
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_bus_init - initialize a HD-audio extended bus
|
||||
* @ebus: the pointer to extended bus object
|
||||
* @dev: device pointer
|
||||
* @ops: bus verb operators
|
||||
* @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use
|
||||
* default ops
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||
const struct hdac_bus_ops *ops,
|
||||
const struct hdac_io_ops *io_ops,
|
||||
const struct hdac_ext_bus_ops *ext_ops)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* check if io ops are provided, if not load the defaults */
|
||||
if (io_ops == NULL)
|
||||
io_ops = &hdac_ext_default_io;
|
||||
|
||||
ret = snd_hdac_bus_init(bus, dev, ops, io_ops);
|
||||
ret = snd_hdac_bus_init(bus, dev, ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@@ -4,12 +4,16 @@
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/export.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include "local.h"
|
||||
#include "trace.h"
|
||||
|
||||
static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
|
||||
|
||||
static const struct hdac_bus_ops default_ops = {
|
||||
.command = snd_hdac_bus_send_cmd,
|
||||
.get_response = snd_hdac_bus_get_response,
|
||||
@@ -19,13 +23,11 @@ static const struct hdac_bus_ops default_ops = {
|
||||
* snd_hdac_bus_init - initialize a HD-audio bas bus
|
||||
* @bus: the pointer to bus object
|
||||
* @ops: bus verb operators
|
||||
* @io_ops: lowlevel I/O operators
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||
const struct hdac_bus_ops *ops,
|
||||
const struct hdac_io_ops *io_ops)
|
||||
const struct hdac_bus_ops *ops)
|
||||
{
|
||||
memset(bus, 0, sizeof(*bus));
|
||||
bus->dev = dev;
|
||||
@@ -33,7 +35,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||
bus->ops = ops;
|
||||
else
|
||||
bus->ops = &default_ops;
|
||||
bus->io_ops = io_ops;
|
||||
bus->dma_type = SNDRV_DMA_TYPE_DEV;
|
||||
INIT_LIST_HEAD(&bus->stream_list);
|
||||
INIT_LIST_HEAD(&bus->codec_list);
|
||||
INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
|
||||
@@ -149,7 +151,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);
|
||||
/*
|
||||
* process queued unsolicited events
|
||||
*/
|
||||
void snd_hdac_bus_process_unsol_events(struct work_struct *work)
|
||||
static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
|
||||
{
|
||||
struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
|
||||
struct hdac_device *codec;
|
||||
@@ -172,7 +174,6 @@ void snd_hdac_bus_process_unsol_events(struct work_struct *work)
|
||||
drv->unsol_event(codec, res);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_process_unsol_events);
|
||||
|
||||
/**
|
||||
* snd_hdac_bus_add_device - Add a codec to bus
|
||||
@@ -197,7 +198,6 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
|
||||
bus->num_codecs++;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
|
||||
|
||||
/**
|
||||
* snd_hdac_bus_remove_device - Remove a codec from bus
|
||||
@@ -216,4 +216,33 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus,
|
||||
bus->num_codecs--;
|
||||
flush_work(&bus->unsol_work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);
|
||||
|
||||
#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
|
||||
/* Helpers for aligned read/write of mmio space, for Tegra */
|
||||
unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask)
|
||||
{
|
||||
void __iomem *aligned_addr =
|
||||
(void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
unsigned int v;
|
||||
|
||||
v = readl(aligned_addr);
|
||||
return (v >> shift) & mask;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_aligned_read);
|
||||
|
||||
void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
|
||||
unsigned int mask)
|
||||
{
|
||||
void __iomem *aligned_addr =
|
||||
(void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
unsigned int v;
|
||||
|
||||
v = readl(aligned_addr);
|
||||
v &= ~(mask << shift);
|
||||
v |= val << shift;
|
||||
writel(v, aligned_addr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_aligned_write);
|
||||
#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
|
||||
|
@@ -447,6 +447,8 @@ static void azx_int_disable(struct hdac_bus *bus)
|
||||
list_for_each_entry(azx_dev, &bus->stream_list, list)
|
||||
snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
|
||||
|
||||
synchronize_irq(bus->irq);
|
||||
|
||||
/* disable SIE for all streams */
|
||||
snd_hdac_chip_writeb(bus, INTCTL, 0);
|
||||
|
||||
@@ -575,12 +577,13 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
|
||||
{
|
||||
struct hdac_stream *s;
|
||||
int num_streams = 0;
|
||||
int dma_type = bus->dma_type ? bus->dma_type : SNDRV_DMA_TYPE_DEV;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(s, &bus->stream_list, list) {
|
||||
/* allocate memory for the BDL for each stream */
|
||||
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
|
||||
BDL_SIZE, &s->bdl);
|
||||
err = snd_dma_alloc_pages(dma_type, bus->dev,
|
||||
BDL_SIZE, &s->bdl);
|
||||
num_streams++;
|
||||
if (err < 0)
|
||||
return -ENOMEM;
|
||||
@@ -589,16 +592,15 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
|
||||
if (WARN_ON(!num_streams))
|
||||
return -EINVAL;
|
||||
/* allocate memory for the position buffer */
|
||||
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
|
||||
num_streams * 8, &bus->posbuf);
|
||||
err = snd_dma_alloc_pages(dma_type, bus->dev,
|
||||
num_streams * 8, &bus->posbuf);
|
||||
if (err < 0)
|
||||
return -ENOMEM;
|
||||
list_for_each_entry(s, &bus->stream_list, list)
|
||||
s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
|
||||
|
||||
/* single page (at least 4096 bytes) must suffice for both ringbuffes */
|
||||
return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
|
||||
PAGE_SIZE, &bus->rb);
|
||||
return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages);
|
||||
|
||||
@@ -612,12 +614,12 @@ void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus)
|
||||
|
||||
list_for_each_entry(s, &bus->stream_list, list) {
|
||||
if (s->bdl.area)
|
||||
bus->io_ops->dma_free_pages(bus, &s->bdl);
|
||||
snd_dma_free_pages(&s->bdl);
|
||||
}
|
||||
|
||||
if (bus->rb.area)
|
||||
bus->io_ops->dma_free_pages(bus, &bus->rb);
|
||||
snd_dma_free_pages(&bus->rb);
|
||||
if (bus->posbuf.area)
|
||||
bus->io_ops->dma_free_pages(bus, &bus->posbuf);
|
||||
snd_dma_free_pages(&bus->posbuf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages);
|
||||
|
@@ -218,8 +218,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
|
||||
*
|
||||
* Return an encoded command verb or -1 for error.
|
||||
*/
|
||||
unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int verb, unsigned int parm)
|
||||
static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int verb, unsigned int parm)
|
||||
{
|
||||
u32 val, addr;
|
||||
|
||||
@@ -237,7 +237,6 @@ unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
|
||||
val |= parm;
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_make_cmd);
|
||||
|
||||
/**
|
||||
* snd_hdac_exec_verb - execute an encoded verb
|
||||
@@ -258,7 +257,6 @@ int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
|
||||
return codec->exec_verb(codec, cmd, flags, res);
|
||||
return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_exec_verb);
|
||||
|
||||
|
||||
/**
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include <sound/core.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_regmap.h>
|
||||
#include "local.h"
|
||||
|
||||
static int codec_pm_lock(struct hdac_device *codec)
|
||||
{
|
||||
|
@@ -229,11 +229,7 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
|
||||
/* set the interrupt enable bits in the descriptor control register */
|
||||
snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
|
||||
|
||||
if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
azx_dev->fifo_size =
|
||||
snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
|
||||
else
|
||||
azx_dev->fifo_size = 0;
|
||||
azx_dev->fifo_size = snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
|
||||
|
||||
/* when LPIB delay correction gives a small negative value,
|
||||
* we ignore it; currently set the threshold statically to
|
||||
@@ -680,8 +676,8 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
|
||||
azx_dev->locked = true;
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
|
||||
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV_SG,
|
||||
byte_size, bufp);
|
||||
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev,
|
||||
byte_size, bufp);
|
||||
if (err < 0)
|
||||
goto err_alloc;
|
||||
|
||||
@@ -707,7 +703,7 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
|
||||
return azx_dev->stream_tag;
|
||||
|
||||
error:
|
||||
bus->io_ops->dma_free_pages(bus, bufp);
|
||||
snd_dma_free_pages(bufp);
|
||||
err_alloc:
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
azx_dev->locked = false;
|
||||
@@ -754,7 +750,7 @@ void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
|
||||
azx_dev->period_bytes = 0;
|
||||
azx_dev->format_val = 0;
|
||||
|
||||
bus->io_ops->dma_free_pages(bus, dmab);
|
||||
snd_dma_free_pages(dmab);
|
||||
dmab->area = NULL;
|
||||
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
|
107
sound/hda/intel-nhlt.c
Звичайний файл
107
sound/hda/intel-nhlt.c
Звичайний файл
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2015-2019 Intel Corporation
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <sound/intel-nhlt.h>
|
||||
|
||||
#define NHLT_ACPI_HEADER_SIG "NHLT"
|
||||
|
||||
/* Unique identification for getting NHLT blobs */
|
||||
static guid_t osc_guid =
|
||||
GUID_INIT(0xA69F886E, 0x6CEB, 0x4594,
|
||||
0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53);
|
||||
|
||||
struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
|
||||
{
|
||||
acpi_handle handle;
|
||||
union acpi_object *obj;
|
||||
struct nhlt_resource_desc *nhlt_ptr;
|
||||
struct nhlt_acpi_table *nhlt_table = NULL;
|
||||
|
||||
handle = ACPI_HANDLE(dev);
|
||||
if (!handle) {
|
||||
dev_err(dev, "Didn't find ACPI_HANDLE\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = acpi_evaluate_dsm(handle, &osc_guid, 1, 1, NULL);
|
||||
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
dev_dbg(dev, "No NHLT table found\n");
|
||||
ACPI_FREE(obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
|
||||
if (nhlt_ptr->length)
|
||||
nhlt_table = (struct nhlt_acpi_table *)
|
||||
memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
|
||||
MEMREMAP_WB);
|
||||
ACPI_FREE(obj);
|
||||
if (nhlt_table &&
|
||||
(strncmp(nhlt_table->header.signature,
|
||||
NHLT_ACPI_HEADER_SIG,
|
||||
strlen(NHLT_ACPI_HEADER_SIG)) != 0)) {
|
||||
memunmap(nhlt_table);
|
||||
dev_err(dev, "NHLT ACPI header signature incorrect\n");
|
||||
return NULL;
|
||||
}
|
||||
return nhlt_table;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_nhlt_init);
|
||||
|
||||
void intel_nhlt_free(struct nhlt_acpi_table *nhlt)
|
||||
{
|
||||
memunmap((void *)nhlt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_nhlt_free);
|
||||
|
||||
int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
|
||||
{
|
||||
struct nhlt_endpoint *epnt;
|
||||
struct nhlt_dmic_array_config *cfg;
|
||||
struct nhlt_vendor_dmic_array_config *cfg_vendor;
|
||||
unsigned int dmic_geo = 0;
|
||||
u8 j;
|
||||
|
||||
if (!nhlt)
|
||||
return 0;
|
||||
|
||||
epnt = (struct nhlt_endpoint *)nhlt->desc;
|
||||
|
||||
for (j = 0; j < nhlt->endpoint_count; j++) {
|
||||
if (epnt->linktype == NHLT_LINK_DMIC) {
|
||||
cfg = (struct nhlt_dmic_array_config *)
|
||||
(epnt->config.caps);
|
||||
switch (cfg->array_type) {
|
||||
case NHLT_MIC_ARRAY_2CH_SMALL:
|
||||
case NHLT_MIC_ARRAY_2CH_BIG:
|
||||
dmic_geo = MIC_ARRAY_2CH;
|
||||
break;
|
||||
|
||||
case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
|
||||
case NHLT_MIC_ARRAY_4CH_L_SHAPED:
|
||||
case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
|
||||
dmic_geo = MIC_ARRAY_4CH;
|
||||
break;
|
||||
case NHLT_MIC_ARRAY_VENDOR_DEFINED:
|
||||
cfg_vendor = (struct nhlt_vendor_dmic_array_config *)cfg;
|
||||
dmic_geo = cfg_vendor->nb_mics;
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "undefined DMIC array_type 0x%0x\n",
|
||||
cfg->array_type);
|
||||
}
|
||||
}
|
||||
epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
|
||||
}
|
||||
|
||||
return dmic_geo;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Intel NHLT driver");
|
@@ -33,4 +33,11 @@ int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid,
|
||||
int num_nodes);
|
||||
void hda_widget_sysfs_exit(struct hdac_device *codec);
|
||||
|
||||
int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
|
||||
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
|
||||
struct hdac_device *codec);
|
||||
|
||||
int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
|
||||
unsigned int flags, unsigned int *res);
|
||||
|
||||
#endif /* __HDAC_LOCAL_H */
|
||||
|
@@ -775,11 +775,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
|
||||
return err;
|
||||
|
||||
memset(&knew, 0, sizeof(knew));
|
||||
knew.name = ak->adc_info[mixer_ch].selector_name;
|
||||
if (!knew.name) {
|
||||
if (!ak->adc_info ||
|
||||
!ak->adc_info[mixer_ch].selector_name) {
|
||||
knew.name = "Capture Channel";
|
||||
knew.index = mixer_ch + ak->idx_offset * 2;
|
||||
}
|
||||
} else
|
||||
knew.name = ak->adc_info[mixer_ch].selector_name;
|
||||
|
||||
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
knew.info = ak4xxx_capture_source_info;
|
||||
|
@@ -80,7 +80,7 @@ int snd_sbdsp_reset(struct snd_sb *chip)
|
||||
|
||||
static int snd_sbdsp_version(struct snd_sb * chip)
|
||||
{
|
||||
unsigned int result = -ENODEV;
|
||||
unsigned int result;
|
||||
|
||||
snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
|
||||
result = (short) snd_sbdsp_get_byte(chip) << 8;
|
||||
|
@@ -788,7 +788,6 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
|
||||
|
||||
dev->patch_status[header->number] |= WF_SLOT_FILLED;
|
||||
|
||||
bptr = buf;
|
||||
bptr = munge_int32 (header->number, buf, 2);
|
||||
munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES);
|
||||
|
||||
|
@@ -1432,25 +1432,25 @@ static int FalconMixerIoctl(u_int cmd, u_long arg)
|
||||
{
|
||||
int data;
|
||||
switch (cmd) {
|
||||
case SOUND_MIXER_READ_RECMASK:
|
||||
case SOUND_MIXER_READ_RECMASK:
|
||||
return IOCTL_OUT(arg, SOUND_MASK_MIC);
|
||||
case SOUND_MIXER_READ_DEVMASK:
|
||||
case SOUND_MIXER_READ_DEVMASK:
|
||||
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER);
|
||||
case SOUND_MIXER_READ_STEREODEVS:
|
||||
case SOUND_MIXER_READ_STEREODEVS:
|
||||
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC);
|
||||
case SOUND_MIXER_READ_VOLUME:
|
||||
case SOUND_MIXER_READ_VOLUME:
|
||||
return IOCTL_OUT(arg,
|
||||
VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
|
||||
VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8);
|
||||
case SOUND_MIXER_READ_CAPS:
|
||||
case SOUND_MIXER_READ_CAPS:
|
||||
return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT);
|
||||
case SOUND_MIXER_WRITE_MIC:
|
||||
case SOUND_MIXER_WRITE_MIC:
|
||||
IOCTL_IN(arg, data);
|
||||
tt_dmasnd.input_gain =
|
||||
RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
|
||||
RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
|
||||
/* fall thru, return set value */
|
||||
case SOUND_MIXER_READ_MIC:
|
||||
/* fall through - return set value */
|
||||
case SOUND_MIXER_READ_MIC:
|
||||
return IOCTL_OUT(arg,
|
||||
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
|
||||
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8);
|
||||
|
@@ -596,11 +596,6 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new snd_ac97_controls_master_mono[2] = {
|
||||
AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
|
||||
AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1)
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new snd_ac97_controls_tone[2] = {
|
||||
AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1),
|
||||
AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
|
||||
|
@@ -2189,11 +2189,10 @@ static int snd_echo_resume(struct device *dev)
|
||||
u32 pipe_alloc_mask;
|
||||
int err;
|
||||
|
||||
commpage_bak = kmalloc(sizeof(*commpage), GFP_KERNEL);
|
||||
commpage = chip->comm_page;
|
||||
commpage_bak = kmemdup(commpage, sizeof(*commpage), GFP_KERNEL);
|
||||
if (commpage_bak == NULL)
|
||||
return -ENOMEM;
|
||||
commpage = chip->comm_page;
|
||||
memcpy(commpage_bak, commpage, sizeof(*commpage));
|
||||
|
||||
err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
|
||||
if (err < 0) {
|
||||
|
@@ -12,6 +12,7 @@ config SND_HDA_INTEL
|
||||
tristate "HD Audio PCI"
|
||||
depends on SND_PCI
|
||||
select SND_HDA
|
||||
select SND_INTEL_NHLT if ACPI
|
||||
help
|
||||
Say Y here to include support for Intel "High Definition
|
||||
Audio" (Azalia) and its compatible devices.
|
||||
@@ -22,10 +23,20 @@ config SND_HDA_INTEL
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-hda-intel.
|
||||
|
||||
config SND_HDA_INTEL_DETECT_DMIC
|
||||
bool "DMIC detection and probe abort"
|
||||
depends on SND_HDA_INTEL
|
||||
help
|
||||
Say Y to detect digital microphones on SKL+ devices. DMICs
|
||||
cannot be handled by the HDaudio legacy driver and are
|
||||
currently only supported by the SOF driver.
|
||||
If unsure say N.
|
||||
|
||||
config SND_HDA_TEGRA
|
||||
tristate "NVIDIA Tegra HD Audio"
|
||||
depends on ARCH_TEGRA
|
||||
select SND_HDA
|
||||
select SND_HDA_ALIGNED_MMIO
|
||||
help
|
||||
Say Y here to support the HDA controller present in NVIDIA
|
||||
Tegra SoCs
|
||||
|
@@ -884,7 +884,8 @@ EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
|
||||
#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC))
|
||||
|
||||
static bool pin_config_match(struct hda_codec *codec,
|
||||
const struct hda_pintbl *pins)
|
||||
const struct hda_pintbl *pins,
|
||||
bool match_all_pins)
|
||||
{
|
||||
const struct hda_pincfg *pin;
|
||||
int i;
|
||||
@@ -908,7 +909,8 @@ static bool pin_config_match(struct hda_codec *codec,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!found && (cfg & 0xf0000000) != 0x40000000)
|
||||
if (match_all_pins &&
|
||||
!found && (cfg & 0xf0000000) != 0x40000000)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -920,10 +922,12 @@ static bool pin_config_match(struct hda_codec *codec,
|
||||
* @codec: the HDA codec
|
||||
* @pin_quirk: zero-terminated pin quirk list
|
||||
* @fixlist: the fixup list
|
||||
* @match_all_pins: all valid pins must match with the table entries
|
||||
*/
|
||||
void snd_hda_pick_pin_fixup(struct hda_codec *codec,
|
||||
const struct snd_hda_pin_quirk *pin_quirk,
|
||||
const struct hda_fixup *fixlist)
|
||||
const struct hda_fixup *fixlist,
|
||||
bool match_all_pins)
|
||||
{
|
||||
const struct snd_hda_pin_quirk *pq;
|
||||
|
||||
@@ -935,7 +939,7 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec,
|
||||
continue;
|
||||
if (codec->core.vendor_id != pq->codec)
|
||||
continue;
|
||||
if (pin_config_match(codec, pq->pins)) {
|
||||
if (pin_config_match(codec, pq->pins, match_all_pins)) {
|
||||
codec->fixup_id = pq->value;
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
codec->fixup_name = pq->name;
|
||||
|
@@ -846,7 +846,13 @@ static void snd_hda_codec_dev_release(struct device *dev)
|
||||
snd_hda_sysfs_clear(codec);
|
||||
kfree(codec->modelname);
|
||||
kfree(codec->wcaps);
|
||||
kfree(codec);
|
||||
|
||||
/*
|
||||
* In the case of ASoC HD-audio, hda_codec is device managed.
|
||||
* It will be freed when the ASoC device is removed.
|
||||
*/
|
||||
if (codec->core.type == HDA_DEV_LEGACY)
|
||||
kfree(codec);
|
||||
}
|
||||
|
||||
#define DEV_NAME_LEN 31
|
||||
|
@@ -794,6 +794,7 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
|
||||
unsigned long timeout;
|
||||
unsigned long loopcounter;
|
||||
int do_poll = 0;
|
||||
bool warned = false;
|
||||
|
||||
again:
|
||||
timeout = jiffies + msecs_to_jiffies(1000);
|
||||
@@ -813,9 +814,17 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
if (time_after(jiffies, timeout))
|
||||
break;
|
||||
if (hbus->needs_damn_long_delay || loopcounter > 3000)
|
||||
#define LOOP_COUNT_MAX 3000
|
||||
if (hbus->needs_damn_long_delay ||
|
||||
loopcounter > LOOP_COUNT_MAX) {
|
||||
if (loopcounter > LOOP_COUNT_MAX && !warned) {
|
||||
dev_dbg_ratelimited(chip->card->dev,
|
||||
"too slow response, last cmd=%#08x\n",
|
||||
bus->last_cmd[addr]);
|
||||
warned = true;
|
||||
}
|
||||
msleep(2); /* temporary workaround */
|
||||
else {
|
||||
} else {
|
||||
udelay(10);
|
||||
cond_resched();
|
||||
}
|
||||
@@ -869,10 +878,13 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
|
||||
*/
|
||||
if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) {
|
||||
hbus->response_reset = 1;
|
||||
dev_err(chip->card->dev,
|
||||
"No response from codec, resetting bus: last cmd=0x%08x\n",
|
||||
bus->last_cmd[addr]);
|
||||
return -EAGAIN; /* give a chance to retry */
|
||||
}
|
||||
|
||||
dev_err(chip->card->dev,
|
||||
dev_WARN(chip->card->dev,
|
||||
"azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
|
||||
bus->last_cmd[addr]);
|
||||
chip->single_cmd = 1;
|
||||
@@ -1207,14 +1219,12 @@ void snd_hda_bus_reset(struct hda_bus *bus)
|
||||
}
|
||||
|
||||
/* HD-audio bus initialization */
|
||||
int azx_bus_init(struct azx *chip, const char *model,
|
||||
const struct hdac_io_ops *io_ops)
|
||||
int azx_bus_init(struct azx *chip, const char *model)
|
||||
{
|
||||
struct hda_bus *bus = &chip->bus;
|
||||
int err;
|
||||
|
||||
err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops,
|
||||
io_ops);
|
||||
err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@@ -206,8 +206,7 @@ void azx_stop_chip(struct azx *chip);
|
||||
irqreturn_t azx_interrupt(int irq, void *dev_id);
|
||||
|
||||
/* Codec interface */
|
||||
int azx_bus_init(struct azx *chip, const char *model,
|
||||
const struct hdac_io_ops *io_ops);
|
||||
int azx_bus_init(struct azx *chip, const char *model);
|
||||
int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
|
||||
int azx_codec_configure(struct azx *chip);
|
||||
int azx_init_streams(struct azx *chip);
|
||||
|
@@ -46,6 +46,7 @@
|
||||
#include <sound/initval.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_i915.h>
|
||||
#include <sound/intel-nhlt.h>
|
||||
#include <linux/vgaarb.h>
|
||||
#include <linux/vga_switcheroo.h>
|
||||
#include <linux/firmware.h>
|
||||
@@ -84,8 +85,6 @@ enum {
|
||||
#define INTEL_SCH_HDA_DEVC 0x78
|
||||
#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
|
||||
|
||||
/* Define IN stream 0 FIFO size offset in VIA controller */
|
||||
#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
|
||||
/* Define VIA HD Audio Device ID*/
|
||||
#define VIA_HDAC_DEVICE_ID 0x3288
|
||||
|
||||
@@ -125,6 +124,7 @@ static char *patch[SNDRV_CARDS];
|
||||
static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
|
||||
CONFIG_SND_HDA_INPUT_BEEP_MODE};
|
||||
#endif
|
||||
static bool dmic_detect = IS_ENABLED(CONFIG_SND_HDA_INTEL_DETECT_DMIC);
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
|
||||
@@ -159,6 +159,8 @@ module_param_array(beep_mode, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
|
||||
"(0=off, 1=on) (default=1).");
|
||||
#endif
|
||||
module_param(dmic_detect, bool, 0444);
|
||||
MODULE_PARM_DESC(dmic_detect, "DMIC detect on SKL+ platforms");
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int param_set_xint(const char *val, const struct kernel_param *kp);
|
||||
@@ -267,6 +269,7 @@ enum {
|
||||
AZX_DRIVER_CTX,
|
||||
AZX_DRIVER_CTHDA,
|
||||
AZX_DRIVER_CMEDIA,
|
||||
AZX_DRIVER_ZHAOXIN,
|
||||
AZX_DRIVER_GENERIC,
|
||||
AZX_NUM_DRIVERS, /* keep this as last entry */
|
||||
};
|
||||
@@ -353,7 +356,7 @@ enum {
|
||||
*/
|
||||
#ifdef SUPPORT_VGA_SWITCHEROO
|
||||
#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
|
||||
#define needs_eld_notify_link(chip) ((chip)->need_eld_notify_link)
|
||||
#define needs_eld_notify_link(chip) ((chip)->bus.keep_power)
|
||||
#else
|
||||
#define use_vga_switcheroo(chip) 0
|
||||
#define needs_eld_notify_link(chip) false
|
||||
@@ -385,6 +388,7 @@ static char *driver_short_names[] = {
|
||||
[AZX_DRIVER_CTX] = "HDA Creative",
|
||||
[AZX_DRIVER_CTHDA] = "HDA Creative",
|
||||
[AZX_DRIVER_CMEDIA] = "HDA C-Media",
|
||||
[AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin",
|
||||
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
|
||||
};
|
||||
|
||||
@@ -811,11 +815,7 @@ static unsigned int azx_via_get_position(struct azx *chip,
|
||||
mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf);
|
||||
mod_dma_pos %= azx_dev->core.period_bytes;
|
||||
|
||||
/* azx_dev->fifo_size can't get FIFO size of in stream.
|
||||
* Get from base address + offset.
|
||||
*/
|
||||
fifo_size = readw(azx_bus(chip)->remap_addr +
|
||||
VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
|
||||
fifo_size = azx_stream(azx_dev)->fifo_size - 1;
|
||||
|
||||
if (azx_dev->insufficient) {
|
||||
/* Link position never gather than FIFO size */
|
||||
@@ -1145,7 +1145,7 @@ static int azx_runtime_idle(struct device *dev)
|
||||
return -EBUSY;
|
||||
|
||||
/* ELD notification gets broken when HD-audio bus is off */
|
||||
if (needs_eld_notify_link(hda))
|
||||
if (needs_eld_notify_link(chip))
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
@@ -1256,7 +1256,7 @@ static void setup_vga_switcheroo_runtime_pm(struct azx *chip)
|
||||
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
|
||||
struct hda_codec *codec;
|
||||
|
||||
if (hda->use_vga_switcheroo && !hda->need_eld_notify_link) {
|
||||
if (hda->use_vga_switcheroo && !needs_eld_notify_link(chip)) {
|
||||
list_for_each_codec(codec, &chip->bus)
|
||||
codec->auto_runtime_pm = 1;
|
||||
/* reset the power save setup */
|
||||
@@ -1270,10 +1270,9 @@ static void azx_vs_gpu_bound(struct pci_dev *pci,
|
||||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
struct azx *chip = card->private_data;
|
||||
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
|
||||
|
||||
if (client_id == VGA_SWITCHEROO_DIS)
|
||||
hda->need_eld_notify_link = 0;
|
||||
chip->bus.keep_power = 0;
|
||||
setup_vga_switcheroo_runtime_pm(chip);
|
||||
}
|
||||
|
||||
@@ -1285,7 +1284,7 @@ static void init_vga_switcheroo(struct azx *chip)
|
||||
dev_info(chip->card->dev,
|
||||
"Handle vga_switcheroo audio client\n");
|
||||
hda->use_vga_switcheroo = 1;
|
||||
hda->need_eld_notify_link = 1; /* cleared in gpu_bound op */
|
||||
chip->bus.keep_power = 1; /* cleared in either gpu_bound op or codec probe */
|
||||
chip->driver_caps |= AZX_DCAPS_PM_RUNTIME;
|
||||
pci_dev_put(p);
|
||||
}
|
||||
@@ -1349,9 +1348,9 @@ static int azx_free(struct azx *chip)
|
||||
}
|
||||
|
||||
if (bus->chip_init) {
|
||||
azx_stop_chip(chip);
|
||||
azx_clear_irq_pending(chip);
|
||||
azx_stop_all_streams(chip);
|
||||
azx_stop_chip(chip);
|
||||
}
|
||||
|
||||
if (bus->irq >= 0)
|
||||
@@ -1684,7 +1683,6 @@ static int default_bdl_pos_adj(struct azx *chip)
|
||||
/*
|
||||
* constructor
|
||||
*/
|
||||
static const struct hdac_io_ops pci_hda_io_ops;
|
||||
static const struct hda_controller_ops pci_hda_ops;
|
||||
|
||||
static int azx_create(struct snd_card *card, struct pci_dev *pci,
|
||||
@@ -1744,13 +1742,17 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
|
||||
else
|
||||
chip->bdl_pos_adj = bdl_pos_adj[dev];
|
||||
|
||||
err = azx_bus_init(chip, model[dev], &pci_hda_io_ops);
|
||||
err = azx_bus_init(chip, model[dev]);
|
||||
if (err < 0) {
|
||||
kfree(hda);
|
||||
pci_disable_device(pci);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* use the non-cached pages in non-snoop mode */
|
||||
if (!azx_snoop(chip))
|
||||
azx_bus(chip)->dma_type = SNDRV_DMA_TYPE_DEV_UC;
|
||||
|
||||
/* Workaround for a communication error on CFL (bko#199007) and CNL */
|
||||
if (IS_CFL(pci) || IS_CNL(pci))
|
||||
azx_bus(chip)->polling_mode = 1;
|
||||
@@ -1985,41 +1987,6 @@ static void azx_firmware_cb(const struct firmware *fw, void *context)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* HDA controller ops.
|
||||
*/
|
||||
|
||||
/* PCI register access. */
|
||||
static void pci_azx_writel(u32 value, u32 __iomem *addr)
|
||||
{
|
||||
writel(value, addr);
|
||||
}
|
||||
|
||||
static u32 pci_azx_readl(u32 __iomem *addr)
|
||||
{
|
||||
return readl(addr);
|
||||
}
|
||||
|
||||
static void pci_azx_writew(u16 value, u16 __iomem *addr)
|
||||
{
|
||||
writew(value, addr);
|
||||
}
|
||||
|
||||
static u16 pci_azx_readw(u16 __iomem *addr)
|
||||
{
|
||||
return readw(addr);
|
||||
}
|
||||
|
||||
static void pci_azx_writeb(u8 value, u8 __iomem *addr)
|
||||
{
|
||||
writeb(value, addr);
|
||||
}
|
||||
|
||||
static u8 pci_azx_readb(u8 __iomem *addr)
|
||||
{
|
||||
return readb(addr);
|
||||
}
|
||||
|
||||
static int disable_msi_reset_irq(struct azx *chip)
|
||||
{
|
||||
struct hdac_bus *bus = azx_bus(chip);
|
||||
@@ -2036,24 +2003,6 @@ static int disable_msi_reset_irq(struct azx *chip)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* DMA page allocation helpers. */
|
||||
static int dma_alloc_pages(struct hdac_bus *bus,
|
||||
int type,
|
||||
size_t size,
|
||||
struct snd_dma_buffer *buf)
|
||||
{
|
||||
struct azx *chip = bus_to_azx(bus);
|
||||
|
||||
if (!azx_snoop(chip) && type == SNDRV_DMA_TYPE_DEV)
|
||||
type = SNDRV_DMA_TYPE_DEV_UC;
|
||||
return snd_dma_alloc_pages(type, bus->dev, size, buf);
|
||||
}
|
||||
|
||||
static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
|
||||
{
|
||||
snd_dma_free_pages(buf);
|
||||
}
|
||||
|
||||
static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *area)
|
||||
{
|
||||
@@ -2065,23 +2014,31 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
|
||||
#endif
|
||||
}
|
||||
|
||||
static const struct hdac_io_ops pci_hda_io_ops = {
|
||||
.reg_writel = pci_azx_writel,
|
||||
.reg_readl = pci_azx_readl,
|
||||
.reg_writew = pci_azx_writew,
|
||||
.reg_readw = pci_azx_readw,
|
||||
.reg_writeb = pci_azx_writeb,
|
||||
.reg_readb = pci_azx_readb,
|
||||
.dma_alloc_pages = dma_alloc_pages,
|
||||
.dma_free_pages = dma_free_pages,
|
||||
};
|
||||
|
||||
static const struct hda_controller_ops pci_hda_ops = {
|
||||
.disable_msi_reset_irq = disable_msi_reset_irq,
|
||||
.pcm_mmap_prepare = pcm_mmap_prepare,
|
||||
.position_check = azx_position_check,
|
||||
};
|
||||
|
||||
static int azx_check_dmic(struct pci_dev *pci, struct azx *chip)
|
||||
{
|
||||
struct nhlt_acpi_table *nhlt;
|
||||
int ret = 0;
|
||||
|
||||
if (chip->driver_type == AZX_DRIVER_SKL &&
|
||||
pci->class != 0x040300) {
|
||||
nhlt = intel_nhlt_init(&pci->dev);
|
||||
if (nhlt) {
|
||||
if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) {
|
||||
ret = -ENODEV;
|
||||
dev_info(&pci->dev, "Digital mics found on Skylake+ platform, aborting probe\n");
|
||||
}
|
||||
intel_nhlt_free(nhlt);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int azx_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
@@ -2112,6 +2069,17 @@ static int azx_probe(struct pci_dev *pci,
|
||||
card->private_data = chip;
|
||||
hda = container_of(chip, struct hda_intel, chip);
|
||||
|
||||
/*
|
||||
* stop probe if digital microphones detected on Skylake+ platform
|
||||
* with the DSP enabled. This is an opt-in behavior defined at build
|
||||
* time or at run-time with a module parameter
|
||||
*/
|
||||
if (dmic_detect) {
|
||||
err = azx_check_dmic(pci, chip);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pci, card);
|
||||
|
||||
err = register_vga_switcheroo(chip);
|
||||
@@ -2653,6 +2621,8 @@ static const struct pci_device_id azx_ids[] = {
|
||||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
.class_mask = 0xffffff,
|
||||
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
|
||||
/* Zhaoxin */
|
||||
{ PCI_DEVICE(0x1d17, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, azx_ids);
|
||||
|
@@ -25,7 +25,6 @@ struct hda_intel {
|
||||
|
||||
/* vga_switcheroo setup */
|
||||
unsigned int use_vga_switcheroo:1;
|
||||
unsigned int need_eld_notify_link:1;
|
||||
unsigned int vga_switcheroo_registered:1;
|
||||
unsigned int init_failed:1; /* delayed init failed */
|
||||
|
||||
|
@@ -361,7 +361,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
|
||||
const struct hda_fixup *fixlist);
|
||||
void snd_hda_pick_pin_fixup(struct hda_codec *codec,
|
||||
const struct snd_hda_pin_quirk *pin_quirk,
|
||||
const struct hda_fixup *fixlist);
|
||||
const struct hda_fixup *fixlist,
|
||||
bool match_all_pins);
|
||||
|
||||
/* helper macros to retrieve pin default-config values */
|
||||
#define get_defcfg_connect(cfg) \
|
||||
|
@@ -75,88 +75,6 @@ MODULE_PARM_DESC(power_save,
|
||||
#define power_save 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DMA page allocation ops.
|
||||
*/
|
||||
static int dma_alloc_pages(struct hdac_bus *bus, int type, size_t size,
|
||||
struct snd_dma_buffer *buf)
|
||||
{
|
||||
return snd_dma_alloc_pages(type, bus->dev, size, buf);
|
||||
}
|
||||
|
||||
static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
|
||||
{
|
||||
snd_dma_free_pages(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register access ops. Tegra HDA register access is DWORD only.
|
||||
*/
|
||||
static void hda_tegra_writel(u32 value, u32 __iomem *addr)
|
||||
{
|
||||
writel(value, addr);
|
||||
}
|
||||
|
||||
static u32 hda_tegra_readl(u32 __iomem *addr)
|
||||
{
|
||||
return readl(addr);
|
||||
}
|
||||
|
||||
static void hda_tegra_writew(u16 value, u16 __iomem *addr)
|
||||
{
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
u32 v;
|
||||
|
||||
v = readl(dword_addr);
|
||||
v &= ~(0xffff << shift);
|
||||
v |= value << shift;
|
||||
writel(v, dword_addr);
|
||||
}
|
||||
|
||||
static u16 hda_tegra_readw(u16 __iomem *addr)
|
||||
{
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
u32 v;
|
||||
|
||||
v = readl(dword_addr);
|
||||
return (v >> shift) & 0xffff;
|
||||
}
|
||||
|
||||
static void hda_tegra_writeb(u8 value, u8 __iomem *addr)
|
||||
{
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
u32 v;
|
||||
|
||||
v = readl(dword_addr);
|
||||
v &= ~(0xff << shift);
|
||||
v |= value << shift;
|
||||
writel(v, dword_addr);
|
||||
}
|
||||
|
||||
static u8 hda_tegra_readb(u8 __iomem *addr)
|
||||
{
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
u32 v;
|
||||
|
||||
v = readl(dword_addr);
|
||||
return (v >> shift) & 0xff;
|
||||
}
|
||||
|
||||
static const struct hdac_io_ops hda_tegra_io_ops = {
|
||||
.reg_writel = hda_tegra_writel,
|
||||
.reg_readl = hda_tegra_readl,
|
||||
.reg_writew = hda_tegra_writew,
|
||||
.reg_readw = hda_tegra_readw,
|
||||
.reg_writeb = hda_tegra_writeb,
|
||||
.reg_readb = hda_tegra_readb,
|
||||
.dma_alloc_pages = dma_alloc_pages,
|
||||
.dma_free_pages = dma_free_pages,
|
||||
};
|
||||
|
||||
static const struct hda_controller_ops hda_tegra_ops; /* nothing special */
|
||||
|
||||
static void hda_tegra_init(struct hda_tegra *hda)
|
||||
@@ -475,7 +393,7 @@ static int hda_tegra_create(struct snd_card *card,
|
||||
|
||||
INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
|
||||
|
||||
err = azx_bus_init(chip, NULL, &hda_tegra_io_ops);
|
||||
err = azx_bus_init(chip, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@@ -119,6 +120,7 @@ struct hdmi_pcm {
|
||||
};
|
||||
|
||||
struct hdmi_spec {
|
||||
struct hda_codec *codec;
|
||||
int num_cvts;
|
||||
struct snd_array cvts; /* struct hdmi_spec_per_cvt */
|
||||
hda_nid_t cvt_nids[4]; /* only for haswell fix */
|
||||
@@ -163,9 +165,11 @@ struct hdmi_spec {
|
||||
struct hda_multi_out multiout;
|
||||
struct hda_pcm_stream pcm_playback;
|
||||
|
||||
/* i915/powerwell (Haswell+/Valleyview+) specific */
|
||||
bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */
|
||||
bool use_jack_detect; /* jack detection enabled */
|
||||
bool use_acomp_notifier; /* use eld_notify callback for hotplug */
|
||||
bool acomp_registered; /* audio component registered in this driver */
|
||||
struct drm_audio_component_audio_ops drm_audio_ops;
|
||||
int (*port2pin)(struct hda_codec *, int); /* reverse port/pin mapping */
|
||||
|
||||
struct hdac_chmap chmap;
|
||||
hda_nid_t vendor_nid;
|
||||
@@ -765,6 +769,10 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
|
||||
static void jack_callback(struct hda_codec *codec,
|
||||
struct hda_jack_callback *jack)
|
||||
{
|
||||
/* stop polling when notification is enabled */
|
||||
if (codec_has_acomp(codec))
|
||||
return;
|
||||
|
||||
/* hda_jack don't support DP MST */
|
||||
check_presence_and_report(codec, jack->nid, 0);
|
||||
}
|
||||
@@ -823,6 +831,9 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
|
||||
|
||||
if (codec_has_acomp(codec))
|
||||
return;
|
||||
|
||||
if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
|
||||
codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
|
||||
return;
|
||||
@@ -1421,7 +1432,7 @@ static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
|
||||
/* update per_pin ELD from the given new ELD;
|
||||
* setup info frame and notification accordingly
|
||||
*/
|
||||
static void update_eld(struct hda_codec *codec,
|
||||
static bool update_eld(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
@@ -1429,7 +1440,7 @@ static void update_eld(struct hda_codec *codec,
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
bool old_eld_valid = pin_eld->eld_valid;
|
||||
bool eld_changed;
|
||||
int pcm_idx = -1;
|
||||
int pcm_idx;
|
||||
|
||||
/* for monitor disconnection, save pcm_idx firstly */
|
||||
pcm_idx = per_pin->pcm_idx;
|
||||
@@ -1452,18 +1463,22 @@ static void update_eld(struct hda_codec *codec,
|
||||
snd_hdmi_show_eld(codec, &eld->info);
|
||||
|
||||
eld_changed = (pin_eld->eld_valid != eld->eld_valid);
|
||||
if (eld->eld_valid && pin_eld->eld_valid)
|
||||
eld_changed |= (pin_eld->monitor_present != eld->monitor_present);
|
||||
if (!eld_changed && eld->eld_valid && pin_eld->eld_valid)
|
||||
if (pin_eld->eld_size != eld->eld_size ||
|
||||
memcmp(pin_eld->eld_buffer, eld->eld_buffer,
|
||||
eld->eld_size) != 0)
|
||||
eld_changed = true;
|
||||
|
||||
pin_eld->monitor_present = eld->monitor_present;
|
||||
pin_eld->eld_valid = eld->eld_valid;
|
||||
pin_eld->eld_size = eld->eld_size;
|
||||
if (eld->eld_valid)
|
||||
memcpy(pin_eld->eld_buffer, eld->eld_buffer, eld->eld_size);
|
||||
pin_eld->info = eld->info;
|
||||
if (eld_changed) {
|
||||
pin_eld->monitor_present = eld->monitor_present;
|
||||
pin_eld->eld_valid = eld->eld_valid;
|
||||
pin_eld->eld_size = eld->eld_size;
|
||||
if (eld->eld_valid)
|
||||
memcpy(pin_eld->eld_buffer, eld->eld_buffer,
|
||||
eld->eld_size);
|
||||
pin_eld->info = eld->info;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-setup pin and infoframe. This is needed e.g. when
|
||||
@@ -1481,6 +1496,7 @@ static void update_eld(struct hda_codec *codec,
|
||||
SNDRV_CTL_EVENT_MASK_VALUE |
|
||||
SNDRV_CTL_EVENT_MASK_INFO,
|
||||
&get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
|
||||
return eld_changed;
|
||||
}
|
||||
|
||||
/* update ELD and jack state via HD-audio verbs */
|
||||
@@ -1582,6 +1598,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld = &spec->temp_eld;
|
||||
struct snd_jack *jack = NULL;
|
||||
bool changed;
|
||||
int size;
|
||||
|
||||
mutex_lock(&per_pin->lock);
|
||||
@@ -1608,15 +1625,13 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
|
||||
* disconnected event. Jack must be fetched before update_eld()
|
||||
*/
|
||||
jack = pin_idx_to_jack(codec, per_pin);
|
||||
update_eld(codec, per_pin, eld);
|
||||
changed = update_eld(codec, per_pin, eld);
|
||||
if (jack == NULL)
|
||||
jack = pin_idx_to_jack(codec, per_pin);
|
||||
if (jack == NULL)
|
||||
goto unlock;
|
||||
snd_jack_report(jack,
|
||||
(eld->monitor_present && eld->eld_valid) ?
|
||||
if (changed && jack)
|
||||
snd_jack_report(jack,
|
||||
(eld->monitor_present && eld->eld_valid) ?
|
||||
SND_JACK_AVOUT : 0);
|
||||
unlock:
|
||||
mutex_unlock(&per_pin->lock);
|
||||
}
|
||||
|
||||
@@ -1632,18 +1647,13 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
||||
snd_hda_power_down_pm(codec);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (codec_has_acomp(codec)) {
|
||||
ret = hdmi_present_sense_via_verbs(per_pin, repoll);
|
||||
snd_hda_power_down_pm(codec);
|
||||
} else {
|
||||
sync_eld_via_acomp(codec, per_pin);
|
||||
ret = false; /* don't call snd_hda_jack_report_sync() */
|
||||
} else {
|
||||
ret = hdmi_present_sense_via_verbs(per_pin, repoll);
|
||||
}
|
||||
|
||||
if (!codec_has_acomp(codec))
|
||||
snd_hda_power_down_pm(codec);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2248,6 +2258,8 @@ static int generic_hdmi_init(struct hda_codec *codec)
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int pin_idx;
|
||||
|
||||
mutex_lock(&spec->pcm_lock);
|
||||
spec->use_jack_detect = !codec->jackpoll_interval;
|
||||
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
hda_nid_t pin_nid = per_pin->pin_nid;
|
||||
@@ -2255,11 +2267,15 @@ static int generic_hdmi_init(struct hda_codec *codec)
|
||||
|
||||
snd_hda_set_dev_select(codec, pin_nid, dev_id);
|
||||
hdmi_init_pin(codec, pin_nid);
|
||||
if (!codec_has_acomp(codec))
|
||||
if (codec_has_acomp(codec))
|
||||
continue;
|
||||
if (spec->use_jack_detect)
|
||||
snd_hda_jack_detect_enable(codec, pin_nid);
|
||||
else
|
||||
snd_hda_jack_detect_enable_callback(codec, pin_nid,
|
||||
codec->jackpoll_interval > 0 ?
|
||||
jack_callback : NULL);
|
||||
jack_callback);
|
||||
}
|
||||
mutex_unlock(&spec->pcm_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2292,7 +2308,9 @@ static void generic_hdmi_free(struct hda_codec *codec)
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int pin_idx, pcm_idx;
|
||||
|
||||
if (codec_has_acomp(codec)) {
|
||||
if (spec->acomp_registered) {
|
||||
snd_hdac_acomp_exit(&codec->bus->core);
|
||||
} else if (codec_has_acomp(codec)) {
|
||||
snd_hdac_acomp_register_notifier(&codec->bus->core, NULL);
|
||||
codec->relaxed_resume = 0;
|
||||
}
|
||||
@@ -2360,6 +2378,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec)
|
||||
if (!spec)
|
||||
return -ENOMEM;
|
||||
|
||||
spec->codec = codec;
|
||||
spec->ops = generic_standard_hdmi_ops;
|
||||
spec->dev_num = 1; /* initialize to 1 */
|
||||
mutex_init(&spec->pcm_lock);
|
||||
@@ -2397,6 +2416,138 @@ static int patch_generic_hdmi(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* generic audio component binding
|
||||
*/
|
||||
|
||||
/* turn on / off the unsol event jack detection dynamically */
|
||||
static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid,
|
||||
bool use_acomp)
|
||||
{
|
||||
struct hda_jack_tbl *tbl;
|
||||
|
||||
tbl = snd_hda_jack_tbl_get(codec, nid);
|
||||
if (tbl) {
|
||||
/* clear unsol even if component notifier is used, or re-enable
|
||||
* if notifier is cleared
|
||||
*/
|
||||
unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag);
|
||||
snd_hda_codec_write_cache(codec, nid, 0,
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE, val);
|
||||
} else {
|
||||
/* if no jack entry was defined beforehand, create a new one
|
||||
* at need (i.e. only when notifier is cleared)
|
||||
*/
|
||||
if (!use_acomp)
|
||||
snd_hda_jack_detect_enable(codec, nid);
|
||||
}
|
||||
}
|
||||
|
||||
/* set up / clear component notifier dynamically */
|
||||
static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
|
||||
bool use_acomp)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
int i;
|
||||
|
||||
spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops);
|
||||
mutex_lock(&spec->pcm_lock);
|
||||
spec->use_acomp_notifier = use_acomp;
|
||||
spec->codec->relaxed_resume = use_acomp;
|
||||
/* reprogram each jack detection logic depending on the notifier */
|
||||
if (spec->use_jack_detect) {
|
||||
for (i = 0; i < spec->num_pins; i++)
|
||||
reprogram_jack_detect(spec->codec,
|
||||
get_pin(spec, i)->pin_nid,
|
||||
use_acomp);
|
||||
}
|
||||
mutex_unlock(&spec->pcm_lock);
|
||||
}
|
||||
|
||||
/* enable / disable the notifier via master bind / unbind */
|
||||
static int generic_acomp_master_bind(struct device *dev,
|
||||
struct drm_audio_component *acomp)
|
||||
{
|
||||
generic_acomp_notifier_set(acomp, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void generic_acomp_master_unbind(struct device *dev,
|
||||
struct drm_audio_component *acomp)
|
||||
{
|
||||
generic_acomp_notifier_set(acomp, false);
|
||||
}
|
||||
|
||||
/* check whether both HD-audio and DRM PCI devices belong to the same bus */
|
||||
static int match_bound_vga(struct device *dev, int subtype, void *data)
|
||||
{
|
||||
struct hdac_bus *bus = data;
|
||||
struct pci_dev *pci, *master;
|
||||
|
||||
if (!dev_is_pci(dev) || !dev_is_pci(bus->dev))
|
||||
return 0;
|
||||
master = to_pci_dev(bus->dev);
|
||||
pci = to_pci_dev(dev);
|
||||
return master->bus == pci->bus;
|
||||
}
|
||||
|
||||
/* audio component notifier for AMD/Nvidia HDMI codecs */
|
||||
static void generic_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id)
|
||||
{
|
||||
struct hda_codec *codec = audio_ptr;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
hda_nid_t pin_nid = spec->port2pin(codec, port);
|
||||
|
||||
if (!pin_nid)
|
||||
return;
|
||||
if (get_wcaps_type(get_wcaps(codec, pin_nid)) != AC_WID_PIN)
|
||||
return;
|
||||
/* skip notification during system suspend (but not in runtime PM);
|
||||
* the state will be updated at resume
|
||||
*/
|
||||
if (snd_power_get_state(codec->card) != SNDRV_CTL_POWER_D0)
|
||||
return;
|
||||
/* ditto during suspend/resume process itself */
|
||||
if (snd_hdac_is_in_pm(&codec->core))
|
||||
return;
|
||||
|
||||
check_presence_and_report(codec, pin_nid, dev_id);
|
||||
}
|
||||
|
||||
/* set up the private drm_audio_ops from the template */
|
||||
static void setup_drm_audio_ops(struct hda_codec *codec,
|
||||
const struct drm_audio_component_audio_ops *ops)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
spec->drm_audio_ops.audio_ptr = codec;
|
||||
/* intel_audio_codec_enable() or intel_audio_codec_disable()
|
||||
* will call pin_eld_notify with using audio_ptr pointer
|
||||
* We need make sure audio_ptr is really setup
|
||||
*/
|
||||
wmb();
|
||||
spec->drm_audio_ops.pin2port = ops->pin2port;
|
||||
spec->drm_audio_ops.pin_eld_notify = ops->pin_eld_notify;
|
||||
spec->drm_audio_ops.master_bind = ops->master_bind;
|
||||
spec->drm_audio_ops.master_unbind = ops->master_unbind;
|
||||
}
|
||||
|
||||
/* initialize the generic HDMI audio component */
|
||||
static void generic_acomp_init(struct hda_codec *codec,
|
||||
const struct drm_audio_component_audio_ops *ops,
|
||||
int (*port2pin)(struct hda_codec *, int))
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
spec->port2pin = port2pin;
|
||||
setup_drm_audio_ops(codec, ops);
|
||||
if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops,
|
||||
match_bound_vga, 0)) {
|
||||
spec->acomp_registered = true;
|
||||
codec->bus->keep_power = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Intel codec parsers and helpers
|
||||
*/
|
||||
@@ -2565,20 +2716,19 @@ static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
|
||||
check_presence_and_report(codec, pin_nid, dev_id);
|
||||
}
|
||||
|
||||
static const struct drm_audio_component_audio_ops intel_audio_ops = {
|
||||
.pin2port = intel_pin2port,
|
||||
.pin_eld_notify = intel_pin_eld_notify,
|
||||
};
|
||||
|
||||
/* register i915 component pin_eld_notify callback */
|
||||
static void register_i915_notifier(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
spec->use_acomp_notifier = true;
|
||||
spec->drm_audio_ops.audio_ptr = codec;
|
||||
/* intel_audio_codec_enable() or intel_audio_codec_disable()
|
||||
* will call pin_eld_notify with using audio_ptr pointer
|
||||
* We need make sure audio_ptr is really setup
|
||||
*/
|
||||
wmb();
|
||||
spec->drm_audio_ops.pin2port = intel_pin2port;
|
||||
spec->drm_audio_ops.pin_eld_notify = intel_pin_eld_notify;
|
||||
spec->port2pin = intel_port2pin;
|
||||
setup_drm_audio_ops(codec, &intel_audio_ops);
|
||||
snd_hdac_acomp_register_notifier(&codec->bus->core,
|
||||
&spec->drm_audio_ops);
|
||||
/* no need for forcible resume for jack check thanks to notifier */
|
||||
@@ -2612,6 +2762,8 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec,
|
||||
/* precondition and allocation for Intel codecs */
|
||||
static int alloc_intel_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* requires i915 binding */
|
||||
if (!codec->bus->core.audio_component) {
|
||||
codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
|
||||
@@ -2620,7 +2772,12 @@ static int alloc_intel_hdmi(struct hda_codec *codec)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return alloc_generic_hdmi(codec);
|
||||
err = alloc_generic_hdmi(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* no need to handle unsol events */
|
||||
codec->patch_ops.unsol_event = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parse and post-process for Intel codecs */
|
||||
@@ -2976,6 +3133,7 @@ static int patch_simple_hdmi(struct hda_codec *codec,
|
||||
if (!spec)
|
||||
return -ENOMEM;
|
||||
|
||||
spec->codec = codec;
|
||||
codec->spec = spec;
|
||||
hdmi_array_init(spec, 1);
|
||||
|
||||
@@ -3280,6 +3438,26 @@ static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* map from pin NID to port; port is 0-based */
|
||||
/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */
|
||||
static int nvhdmi_pin2port(void *audio_ptr, int pin_nid)
|
||||
{
|
||||
return pin_nid - 4;
|
||||
}
|
||||
|
||||
/* reverse-map from port to pin NID: see above */
|
||||
static int nvhdmi_port2pin(struct hda_codec *codec, int port)
|
||||
{
|
||||
return port + 4;
|
||||
}
|
||||
|
||||
static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
|
||||
.pin2port = nvhdmi_pin2port,
|
||||
.pin_eld_notify = generic_acomp_pin_eld_notify,
|
||||
.master_bind = generic_acomp_master_bind,
|
||||
.master_unbind = generic_acomp_master_unbind,
|
||||
};
|
||||
|
||||
static int patch_nvhdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
@@ -3296,6 +3474,8 @@ static int patch_nvhdmi(struct hda_codec *codec)
|
||||
nvhdmi_chmap_cea_alloc_validate_get_type;
|
||||
spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
|
||||
|
||||
generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3783,6 +3963,26 @@ static int atihdmi_init(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* map from pin NID to port; port is 0-based */
|
||||
/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */
|
||||
static int atihdmi_pin2port(void *audio_ptr, int pin_nid)
|
||||
{
|
||||
return pin_nid / 2 - 1;
|
||||
}
|
||||
|
||||
/* reverse-map from port to pin NID: see above */
|
||||
static int atihdmi_port2pin(struct hda_codec *codec, int port)
|
||||
{
|
||||
return port * 2 + 3;
|
||||
}
|
||||
|
||||
static const struct drm_audio_component_audio_ops atihdmi_audio_ops = {
|
||||
.pin2port = atihdmi_pin2port,
|
||||
.pin_eld_notify = generic_acomp_pin_eld_notify,
|
||||
.master_bind = generic_acomp_master_bind,
|
||||
.master_unbind = generic_acomp_master_unbind,
|
||||
};
|
||||
|
||||
static int patch_atihdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
@@ -3831,6 +4031,8 @@ static int patch_atihdmi(struct hda_codec *codec)
|
||||
*/
|
||||
codec->link_down_at_suspend = 1;
|
||||
|
||||
generic_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -1058,6 +1058,9 @@ static const struct snd_pci_quirk beep_white_list[] = {
|
||||
SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1),
|
||||
SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1),
|
||||
SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
|
||||
/* blacklist -- no beep available */
|
||||
SND_PCI_QUIRK(0x17aa, 0x309e, "Lenovo ThinkCentre M73", 0),
|
||||
SND_PCI_QUIRK(0x17aa, 0x30a3, "Lenovo ThinkCentre M93", 0),
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -2841,7 +2844,8 @@ static int patch_alc268(struct hda_codec *codec)
|
||||
return err;
|
||||
|
||||
spec = codec->spec;
|
||||
spec->gen.beep_nid = 0x01;
|
||||
if (has_cdefine_beep(codec))
|
||||
spec->gen.beep_nid = 0x01;
|
||||
|
||||
spec->shutup = alc_eapd_shutup;
|
||||
|
||||
@@ -3755,6 +3759,72 @@ static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
|
||||
vref);
|
||||
}
|
||||
|
||||
/*
|
||||
* Magic sequence to make Huawei Matebook X right speaker working (bko#197801)
|
||||
*/
|
||||
struct hda_alc298_mbxinit {
|
||||
unsigned char value_0x23;
|
||||
unsigned char value_0x25;
|
||||
};
|
||||
|
||||
static void alc298_huawei_mbx_stereo_seq(struct hda_codec *codec,
|
||||
const struct hda_alc298_mbxinit *initval,
|
||||
bool first)
|
||||
{
|
||||
snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x0);
|
||||
alc_write_coef_idx(codec, 0x26, 0xb000);
|
||||
|
||||
if (first)
|
||||
snd_hda_codec_write(codec, 0x21, 0, AC_VERB_GET_PIN_SENSE, 0x0);
|
||||
|
||||
snd_hda_codec_write(codec, 0x6, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
|
||||
alc_write_coef_idx(codec, 0x26, 0xf000);
|
||||
alc_write_coef_idx(codec, 0x23, initval->value_0x23);
|
||||
|
||||
if (initval->value_0x23 != 0x1e)
|
||||
alc_write_coef_idx(codec, 0x25, initval->value_0x25);
|
||||
|
||||
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
|
||||
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
|
||||
}
|
||||
|
||||
static void alc298_fixup_huawei_mbx_stereo(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix,
|
||||
int action)
|
||||
{
|
||||
/* Initialization magic */
|
||||
static const struct hda_alc298_mbxinit dac_init[] = {
|
||||
{0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00},
|
||||
{0x10, 0x00}, {0x1a, 0x40}, {0x1b, 0x82}, {0x1c, 0x00},
|
||||
{0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
|
||||
{0x20, 0xc2}, {0x21, 0xc8}, {0x22, 0x26}, {0x23, 0x24},
|
||||
{0x27, 0xff}, {0x28, 0xff}, {0x29, 0xff}, {0x2a, 0x8f},
|
||||
{0x2b, 0x02}, {0x2c, 0x48}, {0x2d, 0x34}, {0x2e, 0x00},
|
||||
{0x2f, 0x00},
|
||||
{0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
|
||||
{0x34, 0x00}, {0x35, 0x01}, {0x36, 0x93}, {0x37, 0x0c},
|
||||
{0x38, 0x00}, {0x39, 0x00}, {0x3a, 0xf8}, {0x38, 0x80},
|
||||
{}
|
||||
};
|
||||
const struct hda_alc298_mbxinit *seq;
|
||||
|
||||
if (action != HDA_FIXUP_ACT_INIT)
|
||||
return;
|
||||
|
||||
/* Start */
|
||||
snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x00);
|
||||
snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
|
||||
alc_write_coef_idx(codec, 0x26, 0xf000);
|
||||
alc_write_coef_idx(codec, 0x22, 0x31);
|
||||
alc_write_coef_idx(codec, 0x23, 0x0b);
|
||||
alc_write_coef_idx(codec, 0x25, 0x00);
|
||||
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
|
||||
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
|
||||
|
||||
for (seq = dac_init; seq->value_0x23; seq++)
|
||||
alc298_huawei_mbx_stereo_seq(codec, seq, seq == dac_init);
|
||||
}
|
||||
|
||||
static void alc269_fixup_x101_headset_mic(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
@@ -5780,6 +5850,7 @@ enum {
|
||||
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
|
||||
ALC255_FIXUP_DELL_HEADSET_MIC,
|
||||
ALC256_FIXUP_HUAWEI_MACH_WX9_PINS,
|
||||
ALC298_FIXUP_HUAWEI_MBX_STEREO,
|
||||
ALC295_FIXUP_HP_X360,
|
||||
ALC221_FIXUP_HP_HEADSET_MIC,
|
||||
ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
|
||||
@@ -6089,6 +6160,12 @@ static const struct hda_fixup alc269_fixups[] = {
|
||||
.chained = true,
|
||||
.chain_id = ALC255_FIXUP_MIC_MUTE_LED
|
||||
},
|
||||
[ALC298_FIXUP_HUAWEI_MBX_STEREO] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc298_fixup_huawei_mbx_stereo,
|
||||
.chained = true,
|
||||
.chain_id = ALC255_FIXUP_MIC_MUTE_LED
|
||||
},
|
||||
[ALC269_FIXUP_ASUS_X101_FUNC] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc269_fixup_x101_headset_mic,
|
||||
@@ -7280,6 +7357,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
|
||||
{.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"},
|
||||
{.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"},
|
||||
{.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"},
|
||||
{.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"},
|
||||
{}
|
||||
};
|
||||
#define ALC225_STANDARD_PINS \
|
||||
@@ -7590,10 +7668,6 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
||||
{0x12, 0x90a60120},
|
||||
{0x14, 0x90170110},
|
||||
{0x21, 0x0321101f}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
|
||||
{0x12, 0xb7a60130},
|
||||
{0x14, 0x90170110},
|
||||
{0x21, 0x04211020}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
|
||||
ALC290_STANDARD_PINS,
|
||||
{0x15, 0x04211040},
|
||||
@@ -7703,6 +7777,19 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
/* This is the fallback pin_fixup_tbl for alc269 family, to make the tbl match
|
||||
* more machines, don't need to match all valid pins, just need to match
|
||||
* all the pins defined in the tbl. Just because of this reason, it is possible
|
||||
* that a single machine matches multiple tbls, so there is one limitation:
|
||||
* at most one tbl is allowed to define for the same vendor and same codec
|
||||
*/
|
||||
static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = {
|
||||
SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
|
||||
{0x19, 0x40000000},
|
||||
{0x1b, 0x40000000}),
|
||||
{}
|
||||
};
|
||||
|
||||
static void alc269_fill_coef(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
@@ -7892,7 +7979,8 @@ static int patch_alc269(struct hda_codec *codec)
|
||||
|
||||
snd_hda_pick_fixup(codec, alc269_fixup_models,
|
||||
alc269_fixup_tbl, alc269_fixups);
|
||||
snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups);
|
||||
snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups, true);
|
||||
snd_hda_pick_pin_fixup(codec, alc269_fallback_pin_fixup_tbl, alc269_fixups, false);
|
||||
snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl,
|
||||
alc269_fixups);
|
||||
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
|
||||
@@ -8026,7 +8114,8 @@ static int patch_alc861(struct hda_codec *codec)
|
||||
return err;
|
||||
|
||||
spec = codec->spec;
|
||||
spec->gen.beep_nid = 0x23;
|
||||
if (has_cdefine_beep(codec))
|
||||
spec->gen.beep_nid = 0x23;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
spec->power_hook = alc_power_eapd;
|
||||
@@ -8127,7 +8216,8 @@ static int patch_alc861vd(struct hda_codec *codec)
|
||||
return err;
|
||||
|
||||
spec = codec->spec;
|
||||
spec->gen.beep_nid = 0x23;
|
||||
if (has_cdefine_beep(codec))
|
||||
spec->gen.beep_nid = 0x23;
|
||||
|
||||
spec->shutup = alc_eapd_shutup;
|
||||
|
||||
@@ -8267,6 +8357,45 @@ static void alc662_fixup_usi_headset_mic(struct hda_codec *codec,
|
||||
}
|
||||
}
|
||||
|
||||
static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec,
|
||||
struct hda_jack_callback *cb)
|
||||
{
|
||||
/* surround speakers at 0x1b already get muted automatically when
|
||||
* headphones are plugged in, but we have to mute/unmute the remaining
|
||||
* channels manually:
|
||||
* 0x15 - front left/front right
|
||||
* 0x18 - front center/ LFE
|
||||
*/
|
||||
if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) {
|
||||
snd_hda_set_pin_ctl_cache(codec, 0x15, 0);
|
||||
snd_hda_set_pin_ctl_cache(codec, 0x18, 0);
|
||||
} else {
|
||||
snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT);
|
||||
snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
/* Pin 0x1b: shared headphones jack and surround speakers */
|
||||
if (!is_jack_detectable(codec, 0x1b))
|
||||
return;
|
||||
|
||||
switch (action) {
|
||||
case HDA_FIXUP_ACT_PRE_PROBE:
|
||||
snd_hda_jack_detect_enable_callback(codec, 0x1b,
|
||||
alc662_aspire_ethos_mute_speakers);
|
||||
break;
|
||||
case HDA_FIXUP_ACT_INIT:
|
||||
/* Make sure to start in a correct state, i.e. if
|
||||
* headphones have been plugged in before powering up the system
|
||||
*/
|
||||
alc662_aspire_ethos_mute_speakers(codec, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct coef_fw alc668_coefs[] = {
|
||||
WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
|
||||
WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
|
||||
@@ -8338,6 +8467,9 @@ enum {
|
||||
ALC662_FIXUP_USI_FUNC,
|
||||
ALC662_FIXUP_USI_HEADSET_MODE,
|
||||
ALC662_FIXUP_LENOVO_MULTI_CODECS,
|
||||
ALC669_FIXUP_ACER_ASPIRE_ETHOS,
|
||||
ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER,
|
||||
ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET,
|
||||
};
|
||||
|
||||
static const struct hda_fixup alc662_fixups[] = {
|
||||
@@ -8664,6 +8796,33 @@ static const struct hda_fixup alc662_fixups[] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc233_alc662_fixup_lenovo_dual_codecs,
|
||||
},
|
||||
[ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc662_fixup_aspire_ethos_hp,
|
||||
},
|
||||
[ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER] = {
|
||||
.type = HDA_FIXUP_VERBS,
|
||||
/* subwoofer needs an extra GPIO setting to become audible */
|
||||
.v.verbs = (const struct hda_verb[]) {
|
||||
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
|
||||
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
|
||||
{0x01, AC_VERB_SET_GPIO_DATA, 0x00},
|
||||
{ }
|
||||
},
|
||||
.chained = true,
|
||||
.chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET
|
||||
},
|
||||
[ALC669_FIXUP_ACER_ASPIRE_ETHOS] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
{ 0x15, 0x92130110 }, /* front speakers */
|
||||
{ 0x18, 0x99130111 }, /* center/subwoofer */
|
||||
{ 0x1b, 0x11130012 }, /* surround plus jack for HP */
|
||||
{ }
|
||||
},
|
||||
.chained = true,
|
||||
.chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
|
||||
@@ -8709,6 +8868,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68),
|
||||
SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON),
|
||||
SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
|
||||
SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS),
|
||||
|
||||
#if 0
|
||||
/* Below is a quirk table taken from the old code.
|
||||
@@ -8802,6 +8962,7 @@ static const struct hda_model_fixup alc662_fixup_models[] = {
|
||||
{.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"},
|
||||
{.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"},
|
||||
{.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
|
||||
{.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"},
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -8877,7 +9038,7 @@ static int patch_alc662(struct hda_codec *codec)
|
||||
|
||||
snd_hda_pick_fixup(codec, alc662_fixup_models,
|
||||
alc662_fixup_tbl, alc662_fixups);
|
||||
snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups);
|
||||
snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups, true);
|
||||
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
|
||||
|
||||
alc_auto_parse_customize_define(codec);
|
||||
|
@@ -975,15 +975,6 @@ static int stac_create_spdif_mux_ctls(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
static const struct hda_verb stac9200_core_init[] = {
|
||||
/* set dac0mux for dac converter */
|
||||
{ 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct hda_verb stac9200_eapd_init[] = {
|
||||
/* set dac0mux for dac converter */
|
||||
{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
|
||||
|
@@ -49,6 +49,14 @@ static const struct pci_device_id snd_lx6464es_ids[] = {
|
||||
PCI_VENDOR_ID_DIGIGRAM,
|
||||
PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM),
|
||||
}, /* LX6464ES-CAE */
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
|
||||
PCI_VENDOR_ID_DIGIGRAM,
|
||||
PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_SERIAL_SUBSYSTEM),
|
||||
}, /* LX6464ESe */
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
|
||||
PCI_VENDOR_ID_DIGIGRAM,
|
||||
PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_CAE_SERIAL_SUBSYSTEM),
|
||||
}, /* LX6464ESe-CAE */
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
|
@@ -51,7 +51,6 @@ source "sound/soc/dwc/Kconfig"
|
||||
source "sound/soc/fsl/Kconfig"
|
||||
source "sound/soc/hisilicon/Kconfig"
|
||||
source "sound/soc/jz4740/Kconfig"
|
||||
source "sound/soc/nuc900/Kconfig"
|
||||
source "sound/soc/kirkwood/Kconfig"
|
||||
source "sound/soc/img/Kconfig"
|
||||
source "sound/soc/intel/Kconfig"
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o
|
||||
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o
|
||||
snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
|
||||
snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
|
||||
|
||||
@@ -39,7 +39,6 @@ obj-$(CONFIG_SND_SOC) += intel/
|
||||
obj-$(CONFIG_SND_SOC) += mediatek/
|
||||
obj-$(CONFIG_SND_SOC) += meson/
|
||||
obj-$(CONFIG_SND_SOC) += mxs/
|
||||
obj-$(CONFIG_SND_SOC) += nuc900/
|
||||
obj-$(CONFIG_SND_SOC) += kirkwood/
|
||||
obj-$(CONFIG_SND_SOC) += pxa/
|
||||
obj-$(CONFIG_SND_SOC) += qcom/
|
||||
|
@@ -10,7 +10,7 @@ config SND_SOC_AMD_CZ_DA7219MX98357_MACH
|
||||
select SND_SOC_MAX98357A
|
||||
select SND_SOC_ADAU7002
|
||||
select REGULATOR
|
||||
depends on SND_SOC_AMD_ACP && I2C
|
||||
depends on SND_SOC_AMD_ACP && I2C && GPIOLIB
|
||||
help
|
||||
This option enables machine driver for DA7219 and MAX9835.
|
||||
|
||||
|
@@ -1251,8 +1251,7 @@ static int acp_audio_probe(struct platform_device *pdev)
|
||||
if (!audio_drv_data)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
audio_drv_data->acp_mmio = devm_ioremap_resource(&pdev->dev, res);
|
||||
audio_drv_data->acp_mmio = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(audio_drv_data->acp_mmio))
|
||||
return PTR_ERR(audio_drv_data->acp_mmio);
|
||||
|
||||
|
@@ -12,25 +12,31 @@ if SND_ATMEL_SOC
|
||||
config SND_ATMEL_SOC_PDC
|
||||
tristate
|
||||
depends on HAS_DMA
|
||||
default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m
|
||||
default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y)
|
||||
|
||||
config SND_ATMEL_SOC_SSC_PDC
|
||||
tristate
|
||||
|
||||
config SND_ATMEL_SOC_DMA
|
||||
tristate
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
default m if SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=m
|
||||
default y if SND_ATMEL_SOC_SSC_DMA=y || (SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=y)
|
||||
|
||||
config SND_ATMEL_SOC_SSC_DMA
|
||||
tristate
|
||||
|
||||
config SND_ATMEL_SOC_SSC
|
||||
tristate
|
||||
default y if SND_ATMEL_SOC_SSC_DMA=y || SND_ATMEL_SOC_SSC_PDC=y
|
||||
default m if SND_ATMEL_SOC_SSC_DMA=m || SND_ATMEL_SOC_SSC_PDC=m
|
||||
|
||||
config SND_ATMEL_SOC_SSC_PDC
|
||||
tristate "SoC PCM DAI support for AT91 SSC controller using PDC"
|
||||
depends on ATMEL_SSC
|
||||
select SND_ATMEL_SOC_PDC
|
||||
select SND_ATMEL_SOC_SSC
|
||||
help
|
||||
Say Y or M if you want to add support for Atmel SSC interface
|
||||
in PDC mode configured using audio-graph-card in device-tree.
|
||||
|
||||
config SND_ATMEL_SOC_SSC_DMA
|
||||
tristate "SoC PCM DAI support for AT91 SSC controller using DMA"
|
||||
depends on ATMEL_SSC
|
||||
select SND_ATMEL_SOC_DMA
|
||||
select SND_ATMEL_SOC_SSC
|
||||
help
|
||||
Say Y or M if you want to add support for Atmel SSC interface
|
||||
in DMA mode configured using audio-graph-card in device-tree.
|
||||
|
||||
config SND_AT91_SOC_SAM9G20_WM8731
|
||||
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
|
||||
|
@@ -571,11 +571,8 @@ static int atmel_classd_probe(struct platform_device *pdev)
|
||||
dd->pdata = pdata;
|
||||
|
||||
dd->irq = platform_get_irq(pdev, 0);
|
||||
if (dd->irq < 0) {
|
||||
ret = dd->irq;
|
||||
dev_err(dev, "failed to could not get irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (dd->irq < 0)
|
||||
return dd->irq;
|
||||
|
||||
dd->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(dd->pclk)) {
|
||||
|
@@ -612,11 +612,8 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
|
||||
dd->dev = dev;
|
||||
|
||||
dd->irq = platform_get_irq(pdev, 0);
|
||||
if (dd->irq < 0) {
|
||||
ret = dd->irq;
|
||||
dev_err(dev, "failed to get irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (dd->irq < 0)
|
||||
return dd->irq;
|
||||
|
||||
dd->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(dd->pclk)) {
|
||||
|
@@ -471,7 +471,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||
int dir, channels, bits;
|
||||
u32 tfmr, rfmr, tcmr, rcmr;
|
||||
int ret;
|
||||
int fslen, fslen_ext;
|
||||
int fslen, fslen_ext, fs_osync, fs_edge;
|
||||
u32 cmr_div;
|
||||
u32 tcmr_period;
|
||||
u32 rcmr_period;
|
||||
@@ -558,226 +558,45 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||
/*
|
||||
* Compute SSC register settings.
|
||||
*/
|
||||
switch (ssc_p->daifmt
|
||||
& (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
|
||||
|
||||
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
|
||||
fslen_ext = (bits - 1) / 16;
|
||||
fslen = (bits - 1) % 16;
|
||||
|
||||
switch (ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
fs_osync = SSC_FSOS_POSITIVE;
|
||||
fs_edge = SSC_START_RISING_RF;
|
||||
|
||||
rcmr = SSC_BF(RCMR_STTDLY, 0);
|
||||
tcmr = SSC_BF(TCMR_STTDLY, 0);
|
||||
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
fs_osync = SSC_FSOS_NEGATIVE;
|
||||
fs_edge = SSC_START_FALLING_RF;
|
||||
|
||||
rcmr = SSC_BF(RCMR_STTDLY, 1);
|
||||
tcmr = SSC_BF(TCMR_STTDLY, 1);
|
||||
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
/*
|
||||
* I2S format, SSC provides BCLK and LRC clocks.
|
||||
*
|
||||
* The SSC transmit and receive clocks are generated
|
||||
* from the MCK divider, and the BCLK signal
|
||||
* is output on the SSC TK line.
|
||||
*/
|
||||
|
||||
if (bits > 16 && !ssc->pdata->has_fslen_ext) {
|
||||
dev_err(dai->dev,
|
||||
"sample size %d is too large for SSC device\n",
|
||||
bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fslen_ext = (bits - 1) / 16;
|
||||
fslen = (bits - 1) % 16;
|
||||
|
||||
rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
|
||||
| SSC_BF(RCMR_STTDLY, START_DELAY)
|
||||
| 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_CKS_DIV);
|
||||
|
||||
rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
|
||||
| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
|
||||
| SSC_BF(RFMR_FSLEN, fslen)
|
||||
| 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, tcmr_period)
|
||||
| SSC_BF(TCMR_STTDLY, START_DELAY)
|
||||
| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
|
||||
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
||||
| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
|
||||
| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
|
||||
|
||||
tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
|
||||
| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(TFMR_FSDEN, 0)
|
||||
| SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
|
||||
| SSC_BF(TFMR_FSLEN, fslen)
|
||||
| SSC_BF(TFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(TFMR_MSBF)
|
||||
| SSC_BF(TFMR_DATDEF, 0)
|
||||
| SSC_BF(TFMR_DATLEN, (bits - 1));
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
|
||||
/* I2S format, CODEC supplies BCLK and LRC clocks. */
|
||||
rcmr = SSC_BF(RCMR_PERIOD, 0)
|
||||
| SSC_BF(RCMR_STTDLY, START_DELAY)
|
||||
| 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 ?
|
||||
SSC_CKS_PIN : SSC_CKS_CLOCK);
|
||||
|
||||
rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
|
||||
| SSC_BF(RFMR_FSLEN, 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, 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 ?
|
||||
SSC_CKS_CLOCK : SSC_CKS_PIN);
|
||||
|
||||
tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(TFMR_FSDEN, 0)
|
||||
| SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
|
||||
| SSC_BF(TFMR_FSLEN, 0)
|
||||
| SSC_BF(TFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(TFMR_MSBF)
|
||||
| SSC_BF(TFMR_DATDEF, 0)
|
||||
| SSC_BF(TFMR_DATLEN, (bits - 1));
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFS:
|
||||
/* I2S format, CODEC supplies BCLK, SSC supplies LRCLK. */
|
||||
if (bits > 16 && !ssc->pdata->has_fslen_ext) {
|
||||
dev_err(dai->dev,
|
||||
"sample size %d is too large for SSC device\n",
|
||||
bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fslen_ext = (bits - 1) / 16;
|
||||
fslen = (bits - 1) % 16;
|
||||
|
||||
rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
|
||||
| SSC_BF(RCMR_STTDLY, START_DELAY)
|
||||
| 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 ?
|
||||
SSC_CKS_PIN : SSC_CKS_CLOCK);
|
||||
|
||||
rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
|
||||
| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
|
||||
| SSC_BF(RFMR_FSLEN, fslen)
|
||||
| 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, tcmr_period)
|
||||
| SSC_BF(TCMR_STTDLY, START_DELAY)
|
||||
| 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 ?
|
||||
SSC_CKS_CLOCK : SSC_CKS_PIN);
|
||||
|
||||
tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
|
||||
| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_NEGATIVE)
|
||||
| SSC_BF(TFMR_FSDEN, 0)
|
||||
| SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
|
||||
| SSC_BF(TFMR_FSLEN, fslen)
|
||||
| SSC_BF(TFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(TFMR_MSBF)
|
||||
| SSC_BF(TFMR_DATDEF, 0)
|
||||
| SSC_BF(TFMR_DATLEN, (bits - 1));
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
|
||||
/*
|
||||
* DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
|
||||
*
|
||||
* The SSC transmit and receive clocks are generated from the
|
||||
* MCK divider, and the BCLK signal is output
|
||||
* on the SSC TK line.
|
||||
*/
|
||||
rcmr = SSC_BF(RCMR_PERIOD, 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_CKO, SSC_CKO_NONE)
|
||||
| SSC_BF(RCMR_CKS, SSC_CKS_DIV);
|
||||
|
||||
rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE)
|
||||
| SSC_BF(RFMR_FSLEN, 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, tcmr_period)
|
||||
| SSC_BF(TCMR_STTDLY, 1)
|
||||
| SSC_BF(TCMR_START, SSC_START_RISING_RF)
|
||||
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
||||
| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
|
||||
| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
|
||||
|
||||
tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(TFMR_FSDEN, 0)
|
||||
| SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE)
|
||||
| SSC_BF(TFMR_FSLEN, 0)
|
||||
| SSC_BF(TFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(TFMR_MSBF)
|
||||
| SSC_BF(TFMR_DATDEF, 0)
|
||||
| SSC_BF(TFMR_DATLEN, (bits - 1));
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
|
||||
/*
|
||||
* DSP/PCM Mode A format, CODEC supplies BCLK and LRC clocks.
|
||||
* DSP/PCM Mode A format
|
||||
*
|
||||
* Data is transferred on first BCLK after LRC pulse rising
|
||||
* edge.If stereo, the right channel data is contiguous with
|
||||
* the left channel data.
|
||||
*/
|
||||
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_CKO, SSC_CKO_NONE)
|
||||
| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
|
||||
SSC_CKS_PIN : SSC_CKS_CLOCK);
|
||||
fs_osync = SSC_FSOS_POSITIVE;
|
||||
fs_edge = SSC_START_RISING_RF;
|
||||
fslen = fslen_ext = 0;
|
||||
|
||||
rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
|
||||
| SSC_BF(RFMR_FSLEN, 0)
|
||||
| SSC_BF(RFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(RFMR_MSBF)
|
||||
| SSC_BF(RFMR_LOOP, 0)
|
||||
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
||||
rcmr = SSC_BF(RCMR_STTDLY, 1);
|
||||
tcmr = SSC_BF(TCMR_STTDLY, 1);
|
||||
|
||||
tcmr = SSC_BF(TCMR_PERIOD, 0)
|
||||
| SSC_BF(TCMR_STTDLY, START_DELAY)
|
||||
| SSC_BF(TCMR_START, SSC_START_RISING_RF)
|
||||
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
||||
| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
|
||||
| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
|
||||
SSC_CKS_CLOCK : SSC_CKS_PIN);
|
||||
|
||||
tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(TFMR_FSDEN, 0)
|
||||
| SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
|
||||
| SSC_BF(TFMR_FSLEN, 0)
|
||||
| SSC_BF(TFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(TFMR_MSBF)
|
||||
| SSC_BF(TFMR_DATDEF, 0)
|
||||
| SSC_BF(TFMR_DATLEN, (bits - 1));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -785,6 +604,70 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||
ssc_p->daifmt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!atmel_ssc_cfs(ssc_p)) {
|
||||
fslen = fslen_ext = 0;
|
||||
rcmr_period = tcmr_period = 0;
|
||||
fs_osync = SSC_FSOS_NONE;
|
||||
}
|
||||
|
||||
rcmr |= SSC_BF(RCMR_START, fs_edge);
|
||||
tcmr |= SSC_BF(TCMR_START, fs_edge);
|
||||
|
||||
if (atmel_ssc_cbs(ssc_p)) {
|
||||
/*
|
||||
* SSC provides BCLK
|
||||
*
|
||||
* The SSC transmit and receive clocks are generated from the
|
||||
* MCK divider, and the BCLK signal is output
|
||||
* on the SSC TK line.
|
||||
*/
|
||||
rcmr |= SSC_BF(RCMR_CKS, SSC_CKS_DIV)
|
||||
| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
|
||||
|
||||
tcmr |= SSC_BF(TCMR_CKS, SSC_CKS_DIV)
|
||||
| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS);
|
||||
} else {
|
||||
rcmr |= SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
|
||||
SSC_CKS_PIN : SSC_CKS_CLOCK)
|
||||
| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
|
||||
|
||||
tcmr |= SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
|
||||
SSC_CKS_CLOCK : SSC_CKS_PIN)
|
||||
| SSC_BF(TCMR_CKO, SSC_CKO_NONE);
|
||||
}
|
||||
|
||||
rcmr |= SSC_BF(RCMR_PERIOD, rcmr_period)
|
||||
| SSC_BF(RCMR_CKI, SSC_CKI_RISING);
|
||||
|
||||
tcmr |= SSC_BF(TCMR_PERIOD, tcmr_period)
|
||||
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING);
|
||||
|
||||
rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
|
||||
| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(RFMR_FSOS, fs_osync)
|
||||
| SSC_BF(RFMR_FSLEN, fslen)
|
||||
| SSC_BF(RFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(RFMR_MSBF)
|
||||
| SSC_BF(RFMR_LOOP, 0)
|
||||
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
||||
|
||||
tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
|
||||
| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(TFMR_FSDEN, 0)
|
||||
| SSC_BF(TFMR_FSOS, fs_osync)
|
||||
| SSC_BF(TFMR_FSLEN, fslen)
|
||||
| SSC_BF(TFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(TFMR_MSBF)
|
||||
| SSC_BF(TFMR_DATDEF, 0)
|
||||
| SSC_BF(TFMR_DATLEN, (bits - 1));
|
||||
|
||||
if (fslen_ext && !ssc->pdata->has_fslen_ext) {
|
||||
dev_err(dai->dev, "sample size %d is too large for SSC device\n",
|
||||
bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("atmel_ssc_hw_params: "
|
||||
"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
|
||||
rcmr, rfmr, tcmr, tfmr);
|
||||
|
@@ -392,11 +392,11 @@ static int mchp_i2s_mcc_clk_get_rate_diff(struct clk *clk,
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
|
||||
unsigned int bclk, unsigned int *mra)
|
||||
unsigned int bclk, unsigned int *mra,
|
||||
unsigned long *best_rate)
|
||||
{
|
||||
unsigned long clk_rate;
|
||||
unsigned long lcm_rate;
|
||||
unsigned long best_rate = 0;
|
||||
unsigned long best_diff_rate = ~0;
|
||||
unsigned int sysclk;
|
||||
struct clk *best_clk = NULL;
|
||||
@@ -423,7 +423,7 @@ static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
|
||||
(clk_rate == bclk || clk_rate / (bclk * 2) <= GENMASK(5, 0));
|
||||
clk_rate += lcm_rate) {
|
||||
ret = mchp_i2s_mcc_clk_get_rate_diff(dev->gclk, clk_rate,
|
||||
&best_clk, &best_rate,
|
||||
&best_clk, best_rate,
|
||||
&best_diff_rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "gclk error for rate %lu: %d",
|
||||
@@ -437,7 +437,7 @@ static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
|
||||
}
|
||||
|
||||
ret = mchp_i2s_mcc_clk_get_rate_diff(dev->pclk, clk_rate,
|
||||
&best_clk, &best_rate,
|
||||
&best_clk, best_rate,
|
||||
&best_diff_rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "pclk error for rate %lu: %d",
|
||||
@@ -459,33 +459,17 @@ static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
|
||||
|
||||
dev_dbg(dev->dev, "source CLK is %s with rate %lu, diff %lu\n",
|
||||
best_clk == dev->pclk ? "pclk" : "gclk",
|
||||
best_rate, best_diff_rate);
|
||||
|
||||
/* set the rate */
|
||||
ret = clk_set_rate(best_clk, best_rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "unable to set rate %lu to %s: %d\n",
|
||||
best_rate, best_clk == dev->pclk ? "PCLK" : "GCLK",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
*best_rate, best_diff_rate);
|
||||
|
||||
/* Configure divisors */
|
||||
if (dev->sysclk)
|
||||
*mra |= MCHP_I2SMCC_MRA_IMCKDIV(best_rate / (2 * sysclk));
|
||||
*mra |= MCHP_I2SMCC_MRA_ISCKDIV(best_rate / (2 * bclk));
|
||||
*mra |= MCHP_I2SMCC_MRA_IMCKDIV(*best_rate / (2 * sysclk));
|
||||
*mra |= MCHP_I2SMCC_MRA_ISCKDIV(*best_rate / (2 * bclk));
|
||||
|
||||
if (best_clk == dev->gclk) {
|
||||
if (best_clk == dev->gclk)
|
||||
*mra |= MCHP_I2SMCC_MRA_SRCCLK_GCLK;
|
||||
ret = clk_prepare(dev->gclk);
|
||||
if (ret < 0)
|
||||
dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
|
||||
else
|
||||
dev->gclk_use = 1;
|
||||
} else {
|
||||
else
|
||||
*mra |= MCHP_I2SMCC_MRA_SRCCLK_PCLK;
|
||||
dev->gclk_use = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -502,6 +486,7 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
unsigned long rate = 0;
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
u32 mra = 0;
|
||||
u32 mrb = 0;
|
||||
@@ -640,6 +625,17 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (set_divs) {
|
||||
bclk_rate = frame_length * params_rate(params);
|
||||
ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra,
|
||||
&rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev,
|
||||
"unable to configure the divisors: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are already running, the wanted setup must be
|
||||
* the same with the one that's currently ongoing
|
||||
@@ -656,22 +652,35 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mra & MCHP_I2SMCC_MRA_SRCCLK_GCLK && !dev->gclk_use) {
|
||||
/* set the rate */
|
||||
ret = clk_set_rate(dev->gclk, rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev,
|
||||
"unable to set rate %lu to GCLK: %d\n",
|
||||
rate, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare(dev->gclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev->gclk_use = 1;
|
||||
}
|
||||
|
||||
/* Save the number of channels to know what interrupts to enable */
|
||||
dev->channels = channels;
|
||||
|
||||
if (set_divs) {
|
||||
bclk_rate = frame_length * params_rate(params);
|
||||
ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "unable to configure the divisors: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_write(dev->regmap, MCHP_I2SMCC_MRA, mra);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
if (dev->gclk_use) {
|
||||
clk_unprepare(dev->gclk);
|
||||
dev->gclk_use = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return regmap_write(dev->regmap, MCHP_I2SMCC_MRB, mrb);
|
||||
}
|
||||
|
||||
@@ -686,31 +695,37 @@ static int mchp_i2s_mcc_hw_free(struct snd_pcm_substream *substream,
|
||||
err = wait_event_interruptible_timeout(dev->wq_txrdy,
|
||||
dev->tx_rdy,
|
||||
msecs_to_jiffies(500));
|
||||
if (err == 0) {
|
||||
dev_warn_once(dev->dev,
|
||||
"Timeout waiting for Tx ready\n");
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
|
||||
MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels));
|
||||
dev->tx_rdy = 1;
|
||||
}
|
||||
} else {
|
||||
err = wait_event_interruptible_timeout(dev->wq_rxrdy,
|
||||
dev->rx_rdy,
|
||||
msecs_to_jiffies(500));
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
u32 idra;
|
||||
|
||||
dev_warn_once(dev->dev, "Timeout waiting for %s\n",
|
||||
is_playback ? "Tx ready" : "Rx ready");
|
||||
if (is_playback)
|
||||
idra = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
|
||||
else
|
||||
idra = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
|
||||
if (err == 0) {
|
||||
dev_warn_once(dev->dev,
|
||||
"Timeout waiting for Rx ready\n");
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
|
||||
MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
|
||||
dev->rx_rdy = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mchp_i2s_mcc_is_running(dev)) {
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_CR, MCHP_I2SMCC_CR_CKDIS);
|
||||
|
||||
if (dev->gclk_running) {
|
||||
clk_disable_unprepare(dev->gclk);
|
||||
clk_disable(dev->gclk);
|
||||
dev->gclk_running = 0;
|
||||
}
|
||||
if (dev->gclk_use) {
|
||||
clk_unprepare(dev->gclk);
|
||||
dev->gclk_use = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -809,6 +824,8 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai)
|
||||
|
||||
init_waitqueue_head(&dev->wq_txrdy);
|
||||
init_waitqueue_head(&dev->wq_rxrdy);
|
||||
dev->tx_rdy = 1;
|
||||
dev->rx_rdy = 1;
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
|
||||
|
||||
|
@@ -363,7 +363,7 @@ static const struct snd_soc_component_driver au1xpsc_ac97_component = {
|
||||
static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct resource *iores, *dmares;
|
||||
struct resource *dmares;
|
||||
unsigned long sel;
|
||||
struct au1xpsc_audio_data *wd;
|
||||
|
||||
@@ -374,8 +374,7 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
|
||||
|
||||
mutex_init(&wd->lock);
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
|
||||
wd->mmio = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(wd->mmio))
|
||||
return PTR_ERR(wd->mmio);
|
||||
|
||||
|
@@ -291,7 +291,7 @@ static const struct snd_soc_component_driver au1xpsc_i2s_component = {
|
||||
|
||||
static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *iores, *dmares;
|
||||
struct resource *dmares;
|
||||
unsigned long sel;
|
||||
struct au1xpsc_audio_data *wd;
|
||||
|
||||
@@ -300,8 +300,7 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
|
||||
if (!wd)
|
||||
return -ENOMEM;
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
|
||||
wd->mmio = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(wd->mmio))
|
||||
return PTR_ERR(wd->mmio);
|
||||
|
||||
|
@@ -828,7 +828,6 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm2835_i2s_dev *dev;
|
||||
int ret;
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
const __be32 *addr;
|
||||
dma_addr_t dma_base;
|
||||
@@ -848,8 +847,7 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* Request ioarea */
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
|
@@ -639,7 +639,6 @@ static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct cygnus_aio_port *aio;
|
||||
int ret = 0;
|
||||
|
||||
aio = cygnus_dai_get_dma_data(substream);
|
||||
dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
|
||||
@@ -647,7 +646,7 @@ static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
runtime->dma_bytes = params_buffer_bytes(params);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
@@ -668,7 +667,6 @@ static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct cygnus_aio_port *aio;
|
||||
unsigned long bufsize, periodsize;
|
||||
int ret = 0;
|
||||
bool is_play;
|
||||
u32 start;
|
||||
struct ringbuf_regs *p_rbuf = NULL;
|
||||
@@ -693,7 +691,7 @@ static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
|
||||
periodsize, bufsize);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
|
@@ -1342,11 +1342,8 @@ static int cygnus_ssp_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
cygaud->irq_num = platform_get_irq(pdev, 0);
|
||||
if (cygaud->irq_num <= 0) {
|
||||
dev_err(dev, "platform_get_irq failed\n");
|
||||
err = cygaud->irq_num;
|
||||
return err;
|
||||
}
|
||||
if (cygaud->irq_num <= 0)
|
||||
return cygaud->irq_num;
|
||||
|
||||
err = audio_clk_init(pdev, cygaud);
|
||||
if (err) {
|
||||
|
@@ -362,7 +362,6 @@ static const struct snd_soc_component_driver ep93xx_ac97_component = {
|
||||
static int ep93xx_ac97_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ep93xx_ac97_info *info;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
@@ -370,8 +369,7 @@ static int ep93xx_ac97_probe(struct platform_device *pdev)
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
info->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
|
@@ -430,15 +430,13 @@ static const struct snd_soc_component_driver ep93xx_i2s_component = {
|
||||
static int ep93xx_i2s_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ep93xx_i2s_info *info;
|
||||
struct resource *res;
|
||||
int err;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
info->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
|
@@ -529,10 +529,6 @@ static const struct snd_kcontrol_new pm860x_snd_controls[] = {
|
||||
* DAPM Controls
|
||||
*/
|
||||
|
||||
/* PCM Switch / PCM Interface */
|
||||
static const struct snd_kcontrol_new pcm_switch_controls =
|
||||
SOC_DAPM_SINGLE("Switch", PM860X_ADC_EN_2, 0, 1, 0);
|
||||
|
||||
/* AUX1 Switch */
|
||||
static const struct snd_kcontrol_new aux1_switch_controls =
|
||||
SOC_DAPM_SINGLE("Switch", PM860X_ANA_TO_ANA, 4, 1, 0);
|
||||
@@ -549,17 +545,6 @@ static const struct snd_kcontrol_new lepa_switch_controls =
|
||||
static const struct snd_kcontrol_new repa_switch_controls =
|
||||
SOC_DAPM_SINGLE("Switch", PM860X_DAC_EN_2, 1, 1, 0);
|
||||
|
||||
/* PCM Mux / Mux7 */
|
||||
static const char *aif1_text[] = {
|
||||
"PCM L", "PCM R",
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(aif1_enum,
|
||||
PM860X_PCM_IFACE_3, 6, aif1_text);
|
||||
|
||||
static const struct snd_kcontrol_new aif1_mux =
|
||||
SOC_DAPM_ENUM("PCM Mux", aif1_enum);
|
||||
|
||||
/* I2S Mux / Mux9 */
|
||||
static const char *i2s_din_text[] = {
|
||||
"DIN", "DIN1",
|
||||
|
@@ -70,10 +70,12 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_CS43130 if I2C
|
||||
select SND_SOC_CS4341 if SND_SOC_I2C_AND_SPI
|
||||
select SND_SOC_CS4349 if I2C
|
||||
select SND_SOC_CS47L15 if MFD_CS47L15
|
||||
select SND_SOC_CS47L24 if MFD_CS47L24
|
||||
select SND_SOC_CS47L35 if MFD_CS47L35
|
||||
select SND_SOC_CS47L85 if MFD_CS47L85
|
||||
select SND_SOC_CS47L90 if MFD_CS47L90
|
||||
select SND_SOC_CS47L92 if MFD_CS47L92
|
||||
select SND_SOC_CS53L30 if I2C
|
||||
select SND_SOC_CX20442 if TTY
|
||||
select SND_SOC_CX2072X if I2C
|
||||
@@ -197,6 +199,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_TS3A227E if I2C
|
||||
select SND_SOC_TWL4030 if TWL4030_CORE
|
||||
select SND_SOC_TWL6040 if TWL6040_CORE
|
||||
select SND_SOC_UDA1334 if GPIOLIB
|
||||
select SND_SOC_UDA134X
|
||||
select SND_SOC_UDA1380 if I2C
|
||||
select SND_SOC_WCD9335 if SLIMBUS
|
||||
@@ -581,6 +584,9 @@ config SND_SOC_CS4349
|
||||
tristate "Cirrus Logic CS4349 CODEC"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_CS47L15
|
||||
tristate
|
||||
|
||||
config SND_SOC_CS47L24
|
||||
tristate
|
||||
|
||||
@@ -593,6 +599,9 @@ config SND_SOC_CS47L85
|
||||
config SND_SOC_CS47L90
|
||||
tristate
|
||||
|
||||
config SND_SOC_CS47L92
|
||||
tristate
|
||||
|
||||
# Cirrus Logic Quad-Channel ADC
|
||||
config SND_SOC_CS53L30
|
||||
tristate "Cirrus Logic CS53L30 CODEC"
|
||||
@@ -722,12 +731,16 @@ config SND_SOC_LOCHNAGAR_SC
|
||||
|
||||
config SND_SOC_MADERA
|
||||
tristate
|
||||
default y if SND_SOC_CS47L15=y
|
||||
default y if SND_SOC_CS47L35=y
|
||||
default y if SND_SOC_CS47L85=y
|
||||
default y if SND_SOC_CS47L90=y
|
||||
default y if SND_SOC_CS47L92=y
|
||||
default m if SND_SOC_CS47L15=m
|
||||
default m if SND_SOC_CS47L35=m
|
||||
default m if SND_SOC_CS47L85=m
|
||||
default m if SND_SOC_CS47L90=m
|
||||
default m if SND_SOC_CS47L92=m
|
||||
|
||||
config SND_SOC_MAX98088
|
||||
tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec"
|
||||
@@ -1195,6 +1208,14 @@ config SND_SOC_TWL4030
|
||||
config SND_SOC_TWL6040
|
||||
tristate
|
||||
|
||||
config SND_SOC_UDA1334
|
||||
tristate "NXP UDA1334 DAC"
|
||||
depends on GPIOLIB
|
||||
help
|
||||
The UDA1334 is an NXP audio codec, supports the I2S-bus data format
|
||||
and has basic features such as de-emphasis (at 44.1 kHz sampling
|
||||
rate) and mute.
|
||||
|
||||
config SND_SOC_UDA134X
|
||||
tristate
|
||||
|
||||
|
@@ -64,10 +64,12 @@ snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
|
||||
snd-soc-cs43130-objs := cs43130.o
|
||||
snd-soc-cs4341-objs := cs4341.o
|
||||
snd-soc-cs4349-objs := cs4349.o
|
||||
snd-soc-cs47l15-objs := cs47l15.o
|
||||
snd-soc-cs47l24-objs := cs47l24.o
|
||||
snd-soc-cs47l35-objs := cs47l35.o
|
||||
snd-soc-cs47l85-objs := cs47l85.o
|
||||
snd-soc-cs47l90-objs := cs47l90.o
|
||||
snd-soc-cs47l92-objs := cs47l92.o
|
||||
snd-soc-cs53l30-objs := cs53l30.o
|
||||
snd-soc-cx20442-objs := cx20442.o
|
||||
snd-soc-cx2072x-objs := cx2072x.o
|
||||
@@ -210,6 +212,7 @@ snd-soc-tscs454-objs := tscs454.o
|
||||
snd-soc-ts3a227e-objs := ts3a227e.o
|
||||
snd-soc-twl4030-objs := twl4030.o
|
||||
snd-soc-twl6040-objs := twl6040.o
|
||||
snd-soc-uda1334-objs := uda1334.o
|
||||
snd-soc-uda134x-objs := uda134x.o
|
||||
snd-soc-uda1380-objs := uda1380.o
|
||||
snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o
|
||||
@@ -346,9 +349,11 @@ obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.o
|
||||
obj-$(CONFIG_SND_SOC_CS4341) += snd-soc-cs4341.o
|
||||
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
|
||||
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
|
||||
obj-$(CONFIG_SND_SOC_CS47L15) += snd-soc-cs47l15.o
|
||||
obj-$(CONFIG_SND_SOC_CS47L35) += snd-soc-cs47l35.o
|
||||
obj-$(CONFIG_SND_SOC_CS47L85) += snd-soc-cs47l85.o
|
||||
obj-$(CONFIG_SND_SOC_CS47L90) += snd-soc-cs47l90.o
|
||||
obj-$(CONFIG_SND_SOC_CS47L92) += snd-soc-cs47l92.o
|
||||
obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
|
||||
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
|
||||
obj-$(CONFIG_SND_SOC_CX2072X) += snd-soc-cx2072x.o
|
||||
@@ -490,6 +495,7 @@ obj-$(CONFIG_SND_SOC_TSCS454) += snd-soc-tscs454.o
|
||||
obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o
|
||||
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
|
||||
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
|
||||
obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o
|
||||
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
|
||||
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
|
||||
obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o
|
||||
|
@@ -413,15 +413,10 @@ static struct snd_soc_dai_driver ad193x_no_adc_dai = {
|
||||
.ops = &ad193x_dai_ops,
|
||||
};
|
||||
|
||||
struct ad193x_reg_default {
|
||||
unsigned int reg;
|
||||
unsigned int val;
|
||||
};
|
||||
|
||||
/* codec register values to set after reset */
|
||||
static void ad193x_reg_default_init(struct ad193x_priv *ad193x)
|
||||
{
|
||||
const struct ad193x_reg_default reg_init[] = {
|
||||
static const struct reg_sequence reg_init[] = {
|
||||
{ 0, 0x99 }, /* PLL_CLK_CTRL0: pll input: mclki/xi 12.288Mhz */
|
||||
{ 1, 0x04 }, /* PLL_CLK_CTRL1: no on-chip Vref */
|
||||
{ 2, 0x40 }, /* DAC_CTRL0: TDM mode */
|
||||
@@ -437,21 +432,17 @@ static void ad193x_reg_default_init(struct ad193x_priv *ad193x)
|
||||
{ 12, 0x00 }, /* DAC_L4_VOL: no attenuation */
|
||||
{ 13, 0x00 }, /* DAC_R4_VOL: no attenuation */
|
||||
};
|
||||
const struct ad193x_reg_default reg_adc_init[] = {
|
||||
static const struct reg_sequence reg_adc_init[] = {
|
||||
{ 14, 0x03 }, /* ADC_CTRL0: high-pass filter enable */
|
||||
{ 15, 0x43 }, /* ADC_CTRL1: sata delay=1, adc aux mode */
|
||||
{ 16, 0x00 }, /* ADC_CTRL2: reset */
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(reg_init); i++)
|
||||
regmap_write(ad193x->regmap, reg_init[i].reg, reg_init[i].val);
|
||||
regmap_multi_reg_write(ad193x->regmap, reg_init, ARRAY_SIZE(reg_init));
|
||||
|
||||
if (ad193x_has_adc(ad193x)) {
|
||||
for (i = 0; i < ARRAY_SIZE(reg_adc_init); i++) {
|
||||
regmap_write(ad193x->regmap, reg_adc_init[i].reg,
|
||||
reg_adc_init[i].val);
|
||||
}
|
||||
regmap_multi_reg_write(ad193x->regmap, reg_adc_init,
|
||||
ARRAY_SIZE(reg_adc_init));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -334,7 +334,7 @@ static struct cs4271_clk_cfg cs4271_clk_tab[] = {
|
||||
{0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2},
|
||||
};
|
||||
|
||||
#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
|
||||
#define CS4271_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
|
||||
|
||||
static int cs4271_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
@@ -383,13 +383,13 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
|
||||
val = CS4271_MODE1_MODE_4X;
|
||||
|
||||
ratio = cs4271->mclk / cs4271->rate;
|
||||
for (i = 0; i < CS4171_NR_RATIOS; i++)
|
||||
for (i = 0; i < CS4271_NR_RATIOS; i++)
|
||||
if ((cs4271_clk_tab[i].master == cs4271->master) &&
|
||||
(cs4271_clk_tab[i].speed_mode == val) &&
|
||||
(cs4271_clk_tab[i].ratio == ratio))
|
||||
break;
|
||||
|
||||
if (i == CS4171_NR_RATIOS) {
|
||||
if (i == CS4271_NR_RATIOS) {
|
||||
dev_err(component->dev, "Invalid sample rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@@ -199,14 +199,6 @@ static const struct soc_enum beep_bass_enum =
|
||||
SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 1,
|
||||
ARRAY_SIZE(beep_bass_text), beep_bass_text);
|
||||
|
||||
static const char * const adc_swap_text[] = {
|
||||
"None", "A+B/2", "A-B/2", "Swap"
|
||||
};
|
||||
|
||||
static const struct soc_enum adc_swap_enum =
|
||||
SOC_ENUM_SINGLE(CS42L56_MISC_ADC_CTL, 3,
|
||||
ARRAY_SIZE(adc_swap_text), adc_swap_text);
|
||||
|
||||
static const char * const pgaa_mux_text[] = {
|
||||
"AIN1A", "AIN2A", "AIN3A"};
|
||||
|
||||
|
@@ -273,12 +273,6 @@ static SOC_ENUM_SINGLE_DECL(xsp_output_mux_enum,
|
||||
CS42L73_MIXERCTL, 4,
|
||||
cs42l73_spo_mixer_text);
|
||||
|
||||
static const struct snd_kcontrol_new vsp_output_mux =
|
||||
SOC_DAPM_ENUM("Route", vsp_output_mux_enum);
|
||||
|
||||
static const struct snd_kcontrol_new xsp_output_mux =
|
||||
SOC_DAPM_ENUM("Route", xsp_output_mux_enum);
|
||||
|
||||
static const struct snd_kcontrol_new hp_amp_ctl =
|
||||
SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 0, 1, 1);
|
||||
|
||||
|
@@ -684,6 +684,8 @@ static int cs42xx8_runtime_suspend(struct device *dev)
|
||||
#endif
|
||||
|
||||
const struct dev_pm_ops cs42xx8_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL)
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(cs42xx8_pm);
|
||||
|
@@ -378,6 +378,7 @@ static struct i2c_driver cs4349_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "cs4349",
|
||||
.of_match_table = cs4349_of_match,
|
||||
.pm = &cs4349_runtime_pm,
|
||||
},
|
||||
.id_table = cs4349_i2c_id,
|
||||
.probe = cs4349_i2c_probe,
|
||||
|
1490
sound/soc/codecs/cs47l15.c
Звичайний файл
1490
sound/soc/codecs/cs47l15.c
Звичайний файл
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
@@ -524,7 +524,7 @@ SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK,
|
||||
MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, 6,
|
||||
SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT,
|
||||
0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
|
||||
|
@@ -2402,13 +2402,6 @@ static irqreturn_t cs47l90_adsp2_irq(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t cs47l90_dsp_bus_error(int irq, void *data)
|
||||
{
|
||||
struct wm_adsp *dsp = (struct wm_adsp *)data;
|
||||
|
||||
return wm_adsp2_bus_error(dsp);
|
||||
}
|
||||
|
||||
static int cs47l90_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component);
|
||||
@@ -2558,7 +2551,7 @@ static int cs47l90_probe(struct platform_device *pdev)
|
||||
|
||||
if (ret == 0) {
|
||||
ret = madera_init_bus_error_irq(&cs47l90->core, i,
|
||||
cs47l90_dsp_bus_error);
|
||||
wm_adsp2_bus_error);
|
||||
if (ret != 0)
|
||||
wm_adsp2_remove(&cs47l90->core.adsp[i]);
|
||||
}
|
||||
|
2039
sound/soc/codecs/cs47l92.c
Звичайний файл
2039
sound/soc/codecs/cs47l92.c
Звичайний файл
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
@@ -33,6 +34,7 @@ static const unsigned int supported_mclk_lrck_ratios[] = {
|
||||
|
||||
struct es8316_priv {
|
||||
struct mutex lock;
|
||||
struct clk *mclk;
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_component *component;
|
||||
struct snd_soc_jack *jack;
|
||||
@@ -51,7 +53,10 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_target_tlv, -1650, 150, 0);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(hpmixer_gain_tlv, -1200, 150, 0);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpmixer_gain_tlv,
|
||||
0, 4, TLV_DB_SCALE_ITEM(-1200, 150, 0),
|
||||
8, 11, TLV_DB_SCALE_ITEM(-450, 150, 0),
|
||||
);
|
||||
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(adc_pga_gain_tlv,
|
||||
0, 0, TLV_DB_SCALE_ITEM(-350, 0, 0),
|
||||
@@ -89,7 +94,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = {
|
||||
SOC_DOUBLE_TLV("Headphone Playback Volume", ES8316_CPHP_ICAL_VOL,
|
||||
4, 0, 3, 1, hpout_vol_tlv),
|
||||
SOC_DOUBLE_TLV("Headphone Mixer Volume", ES8316_HPMIX_VOL,
|
||||
0, 4, 7, 0, hpmixer_gain_tlv),
|
||||
4, 0, 11, 0, hpmixer_gain_tlv),
|
||||
|
||||
SOC_ENUM("Playback Polarity", dacpol),
|
||||
SOC_DOUBLE_R_TLV("DAC Playback Volume", ES8316_DAC_VOLL,
|
||||
@@ -360,13 +365,21 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||
{
|
||||
struct snd_soc_component *component = codec_dai->component;
|
||||
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||
int i;
|
||||
int i, ret;
|
||||
int count = 0;
|
||||
|
||||
es8316->sysclk = freq;
|
||||
|
||||
if (freq == 0)
|
||||
if (freq == 0) {
|
||||
es8316->sysclk_constraints.list = NULL;
|
||||
es8316->sysclk_constraints.count = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(es8316->mclk, freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Limit supported sample rates to ones that can be autodetected
|
||||
* by the codec running in slave mode.
|
||||
@@ -441,17 +454,10 @@ static int es8316_pcm_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (es8316->sysclk == 0) {
|
||||
dev_err(component->dev, "No sysclk provided\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The set of sample rates that can be supported depends on the
|
||||
* MCLK supplied to the CODEC.
|
||||
*/
|
||||
snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&es8316->sysclk_constraints);
|
||||
if (es8316->sysclk_constraints.list)
|
||||
snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&es8316->sysclk_constraints);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -463,11 +469,19 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||
u8 wordlen = 0;
|
||||
int i;
|
||||
|
||||
if (!es8316->sysclk) {
|
||||
dev_err(component->dev, "No MCLK configured\n");
|
||||
return -EINVAL;
|
||||
/* Validate supported sample rates that are autodetected from MCLK */
|
||||
for (i = 0; i < NR_SUPPORTED_MCLK_LRCK_RATIOS; i++) {
|
||||
const unsigned int ratio = supported_mclk_lrck_ratios[i];
|
||||
|
||||
if (es8316->sysclk % ratio != 0)
|
||||
continue;
|
||||
if (es8316->sysclk / ratio == params_rate(params))
|
||||
break;
|
||||
}
|
||||
if (i == NR_SUPPORTED_MCLK_LRCK_RATIOS)
|
||||
return -EINVAL;
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
@@ -697,9 +711,24 @@ static int es8316_set_jack(struct snd_soc_component *component,
|
||||
static int es8316_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
es8316->component = component;
|
||||
|
||||
es8316->mclk = devm_clk_get_optional(component->dev, "mclk");
|
||||
if (IS_ERR(es8316->mclk)) {
|
||||
dev_err(component->dev, "unable to get mclk\n");
|
||||
return PTR_ERR(es8316->mclk);
|
||||
}
|
||||
if (!es8316->mclk)
|
||||
dev_warn(component->dev, "assuming static mclk\n");
|
||||
|
||||
ret = clk_prepare_enable(es8316->mclk);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "unable to enable mclk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset codec and enable current state machine */
|
||||
snd_soc_component_write(component, ES8316_RESET, 0x3f);
|
||||
usleep_range(5000, 5500);
|
||||
@@ -722,8 +751,16 @@ static int es8316_probe(struct snd_soc_component *component)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void es8316_remove(struct snd_soc_component *component)
|
||||
{
|
||||
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
clk_disable_unprepare(es8316->mclk);
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver soc_component_dev_es8316 = {
|
||||
.probe = es8316_probe,
|
||||
.remove = es8316_remove,
|
||||
.set_jack = es8316_set_jack,
|
||||
.controls = es8316_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(es8316_snd_controls),
|
||||
|
@@ -99,7 +99,6 @@ static SOC_ENUM_SINGLE_DECL(adcpol,
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
|
||||
|
||||
@@ -228,7 +227,7 @@ static const struct soc_enum es8328_rline_enum =
|
||||
ARRAY_SIZE(es8328_line_texts),
|
||||
es8328_line_texts);
|
||||
static const struct snd_kcontrol_new es8328_right_line_controls =
|
||||
SOC_DAPM_ENUM("Route", es8328_lline_enum);
|
||||
SOC_DAPM_ENUM("Route", es8328_rline_enum);
|
||||
|
||||
/* Left Mixer */
|
||||
static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
|
||||
|
@@ -495,6 +495,10 @@ static int hdac_hda_dev_probe(struct hdac_device *hdev)
|
||||
|
||||
static int hdac_hda_dev_remove(struct hdac_device *hdev)
|
||||
{
|
||||
struct hdac_hda_priv *hda_pvt;
|
||||
|
||||
hda_pvt = dev_get_drvdata(&hdev->dev);
|
||||
cancel_delayed_work_sync(&hda_pvt->codec.jackpoll_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -88,8 +88,10 @@ struct hdac_hdmi_port {
|
||||
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
|
||||
struct hdac_hdmi_eld eld;
|
||||
const char *jack_pin;
|
||||
bool is_connect;
|
||||
struct snd_soc_dapm_context *dapm;
|
||||
const char *output_pin;
|
||||
struct work_struct dapm_work;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_pcm {
|
||||
@@ -163,11 +165,7 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
|
||||
{
|
||||
struct hdac_device *hdev = port->pin->hdev;
|
||||
|
||||
if (is_connect)
|
||||
snd_soc_dapm_enable_pin(port->dapm, port->jack_pin);
|
||||
else
|
||||
snd_soc_dapm_disable_pin(port->dapm, port->jack_pin);
|
||||
|
||||
port->is_connect = is_connect;
|
||||
if (is_connect) {
|
||||
/*
|
||||
* Report Jack connect event when a device is connected
|
||||
@@ -193,10 +191,32 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
|
||||
if (pcm->jack_event > 0)
|
||||
pcm->jack_event--;
|
||||
}
|
||||
}
|
||||
|
||||
static void hdac_hdmi_port_dapm_update(struct hdac_hdmi_port *port)
|
||||
{
|
||||
if (port->is_connect)
|
||||
snd_soc_dapm_enable_pin(port->dapm, port->jack_pin);
|
||||
else
|
||||
snd_soc_dapm_disable_pin(port->dapm, port->jack_pin);
|
||||
snd_soc_dapm_sync(port->dapm);
|
||||
}
|
||||
|
||||
static void hdac_hdmi_jack_dapm_work(struct work_struct *work)
|
||||
{
|
||||
struct hdac_hdmi_port *port;
|
||||
|
||||
port = container_of(work, struct hdac_hdmi_port, dapm_work);
|
||||
hdac_hdmi_port_dapm_update(port);
|
||||
}
|
||||
|
||||
static void hdac_hdmi_jack_report_sync(struct hdac_hdmi_pcm *pcm,
|
||||
struct hdac_hdmi_port *port, bool is_connect)
|
||||
{
|
||||
hdac_hdmi_jack_report(pcm, port, is_connect);
|
||||
hdac_hdmi_port_dapm_update(port);
|
||||
}
|
||||
|
||||
/* MST supported verbs */
|
||||
/*
|
||||
* Get the no devices that can be connected to a port on the Pin widget.
|
||||
@@ -904,7 +924,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
|
||||
list_for_each_entry_safe(p, p_next, &pcm->port_list, head) {
|
||||
if (p == port && p->id == port->id &&
|
||||
p->pin == port->pin) {
|
||||
hdac_hdmi_jack_report(pcm, port, false);
|
||||
hdac_hdmi_jack_report_sync(pcm, port, false);
|
||||
list_del(&p->head);
|
||||
}
|
||||
}
|
||||
@@ -918,7 +938,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
|
||||
if (!strcmp(cvt_name, pcm->cvt->name)) {
|
||||
list_add_tail(&port->head, &pcm->port_list);
|
||||
if (port->eld.monitor_present && port->eld.eld_valid) {
|
||||
hdac_hdmi_jack_report(pcm, port, true);
|
||||
hdac_hdmi_jack_report_sync(pcm, port, true);
|
||||
mutex_unlock(&hdmi->pin_mutex);
|
||||
return ret;
|
||||
}
|
||||
@@ -1281,16 +1301,20 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
|
||||
* report jack here. It will be done in usermode mux
|
||||
* control select.
|
||||
*/
|
||||
if (pcm)
|
||||
if (pcm) {
|
||||
hdac_hdmi_jack_report(pcm, port, false);
|
||||
schedule_work(&port->dapm_work);
|
||||
}
|
||||
|
||||
mutex_unlock(&hdmi->pin_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (port->eld.monitor_present && port->eld.eld_valid) {
|
||||
if (pcm)
|
||||
if (pcm) {
|
||||
hdac_hdmi_jack_report(pcm, port, true);
|
||||
schedule_work(&port->dapm_work);
|
||||
}
|
||||
|
||||
print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1,
|
||||
port->eld.eld_buffer, port->eld.eld_size, false);
|
||||
@@ -1319,6 +1343,7 @@ static int hdac_hdmi_add_ports(struct hdac_device *hdev,
|
||||
for (i = 0; i < max_ports; i++) {
|
||||
ports[i].id = i;
|
||||
ports[i].pin = pin;
|
||||
INIT_WORK(&ports[i].dapm_work, hdac_hdmi_jack_dapm_work);
|
||||
}
|
||||
pin->ports = ports;
|
||||
pin->num_ports = max_ports;
|
||||
@@ -2083,8 +2108,20 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void clear_dapm_works(struct hdac_device *hdev)
|
||||
{
|
||||
struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
|
||||
struct hdac_hdmi_pin *pin;
|
||||
int i;
|
||||
|
||||
list_for_each_entry(pin, &hdmi->pin_list, head)
|
||||
for (i = 0; i < pin->num_ports; i++)
|
||||
cancel_work_sync(&pin->ports[i].dapm_work);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_dev_remove(struct hdac_device *hdev)
|
||||
{
|
||||
clear_dapm_works(hdev);
|
||||
snd_hdac_display_power(hdev->bus, hdev->addr, false);
|
||||
|
||||
return 0;
|
||||
@@ -2103,6 +2140,8 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
|
||||
if (!bus)
|
||||
return 0;
|
||||
|
||||
clear_dapm_works(hdev);
|
||||
|
||||
/*
|
||||
* Power down afg.
|
||||
* codec_read is preferred over codec_write to set the power state.
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
@@ -274,6 +275,8 @@ struct hdmi_codec_priv {
|
||||
struct snd_pcm_chmap *chmap_info;
|
||||
unsigned int chmap_idx;
|
||||
struct mutex lock;
|
||||
struct snd_soc_jack *jack;
|
||||
unsigned int jack_status;
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
|
||||
@@ -663,6 +666,49 @@ static int hdmi_dai_probe(struct snd_soc_dai *dai)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp,
|
||||
unsigned int jack_status)
|
||||
{
|
||||
if (hcp->jack && jack_status != hcp->jack_status) {
|
||||
snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT);
|
||||
hcp->jack_status = jack_status;
|
||||
}
|
||||
}
|
||||
|
||||
static void plugged_cb(struct device *dev, bool plugged)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = dev_get_drvdata(dev);
|
||||
|
||||
if (plugged)
|
||||
hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT);
|
||||
else
|
||||
hdmi_codec_jack_report(hcp, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* hdmi_codec_set_jack_detect - register HDMI plugged callback
|
||||
* @component: the hdmi-codec instance
|
||||
* @jack: ASoC jack to report (dis)connection events on
|
||||
*/
|
||||
int hdmi_codec_set_jack_detect(struct snd_soc_component *component,
|
||||
struct snd_soc_jack *jack)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
if (hcp->hcd.ops->hook_plugged_cb) {
|
||||
hcp->jack = jack;
|
||||
ret = hcp->hcd.ops->hook_plugged_cb(component->dev->parent,
|
||||
hcp->hcd.data,
|
||||
plugged_cb,
|
||||
component->dev);
|
||||
if (ret)
|
||||
hcp->jack = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hdmi_codec_set_jack_detect);
|
||||
|
||||
static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdmi_codec_daifmt *cf = dai->playback_dma_data;
|
||||
|
@@ -405,7 +405,6 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rk3036_codec_priv *priv;
|
||||
struct device_node *of_node = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
struct regmap *grf;
|
||||
int ret;
|
||||
@@ -414,8 +413,7 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
|
@@ -545,15 +545,13 @@ static int jz4725b_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct jz_icdc *icdc;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
|
||||
icdc = devm_kzalloc(dev, sizeof(*icdc), GFP_KERNEL);
|
||||
if (!icdc)
|
||||
return -ENOMEM;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
icdc->base = devm_ioremap_resource(dev, mem);
|
||||
icdc->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(icdc->base))
|
||||
return PTR_ERR(icdc->base);
|
||||
|
||||
|
@@ -318,7 +318,6 @@ static int jz4740_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct jz4740_codec *jz4740_codec;
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
|
||||
jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec),
|
||||
@@ -326,8 +325,7 @@ static int jz4740_codec_probe(struct platform_device *pdev)
|
||||
if (!jz4740_codec)
|
||||
return -ENOMEM;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
|
@@ -87,6 +87,16 @@
|
||||
#define MADERA_FLLAO_MIN_N 4
|
||||
#define MADERA_FLLAO_MAX_N 1023
|
||||
#define MADERA_FLLAO_MAX_FBDIV 254
|
||||
#define MADERA_FLLHJ_INT_MAX_N 1023
|
||||
#define MADERA_FLLHJ_INT_MIN_N 1
|
||||
#define MADERA_FLLHJ_FRAC_MAX_N 255
|
||||
#define MADERA_FLLHJ_FRAC_MIN_N 4
|
||||
#define MADERA_FLLHJ_LOW_THRESH 192000
|
||||
#define MADERA_FLLHJ_MID_THRESH 1152000
|
||||
#define MADERA_FLLHJ_MAX_THRESH 13000000
|
||||
#define MADERA_FLLHJ_LOW_GAINS 0x23f0
|
||||
#define MADERA_FLLHJ_MID_GAINS 0x22f2
|
||||
#define MADERA_FLLHJ_HIGH_GAINS 0x21f0
|
||||
|
||||
#define MADERA_FLL_SYNCHRONISER_OFFS 0x10
|
||||
#define CS47L35_FLL_SYNCHRONISER_OFFS 0xE
|
||||
@@ -96,6 +106,7 @@
|
||||
#define MADERA_FLL_CONTROL_4_OFFS 0x4
|
||||
#define MADERA_FLL_CONTROL_5_OFFS 0x5
|
||||
#define MADERA_FLL_CONTROL_6_OFFS 0x6
|
||||
#define MADERA_FLL_GAIN_OFFS 0x8
|
||||
#define MADERA_FLL_CONTROL_7_OFFS 0x9
|
||||
#define MADERA_FLL_EFS_2_OFFS 0xA
|
||||
#define MADERA_FLL_SYNCHRONISER_1_OFFS 0x1
|
||||
@@ -107,6 +118,9 @@
|
||||
#define MADERA_FLL_SYNCHRONISER_7_OFFS 0x7
|
||||
#define MADERA_FLL_SPREAD_SPECTRUM_OFFS 0x9
|
||||
#define MADERA_FLL_GPIO_CLOCK_OFFS 0xA
|
||||
#define MADERA_FLL_CONTROL_10_OFFS 0xA
|
||||
#define MADERA_FLL_CONTROL_11_OFFS 0xB
|
||||
#define MADERA_FLL1_DIGITAL_TEST_1_OFFS 0xD
|
||||
|
||||
#define MADERA_FLLAO_CONTROL_1_OFFS 0x1
|
||||
#define MADERA_FLLAO_CONTROL_2_OFFS 0x2
|
||||
@@ -300,6 +314,100 @@ int madera_free_overheat(struct madera_priv *priv)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(madera_free_overheat);
|
||||
|
||||
static int madera_get_variable_u32_array(struct device *dev,
|
||||
const char *propname,
|
||||
u32 *dest, int n_max,
|
||||
int multiple)
|
||||
{
|
||||
int n, ret;
|
||||
|
||||
n = device_property_count_u32(dev, propname);
|
||||
if (n < 0) {
|
||||
if (n == -EINVAL)
|
||||
return 0; /* missing, ignore */
|
||||
|
||||
dev_warn(dev, "%s malformed (%d)\n", propname, n);
|
||||
|
||||
return n;
|
||||
} else if ((n % multiple) != 0) {
|
||||
dev_warn(dev, "%s not a multiple of %d entries\n",
|
||||
propname, multiple);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (n > n_max)
|
||||
n = n_max;
|
||||
|
||||
ret = device_property_read_u32_array(dev, propname, dest, n);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void madera_prop_get_inmode(struct madera_priv *priv)
|
||||
{
|
||||
struct madera *madera = priv->madera;
|
||||
struct madera_codec_pdata *pdata = &madera->pdata.codec;
|
||||
u32 tmp[MADERA_MAX_INPUT * MADERA_MAX_MUXED_CHANNELS];
|
||||
int n, i, in_idx, ch_idx;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(pdata->inmode) != MADERA_MAX_INPUT);
|
||||
BUILD_BUG_ON(ARRAY_SIZE(pdata->inmode[0]) != MADERA_MAX_MUXED_CHANNELS);
|
||||
|
||||
n = madera_get_variable_u32_array(madera->dev, "cirrus,inmode",
|
||||
tmp, ARRAY_SIZE(tmp),
|
||||
MADERA_MAX_MUXED_CHANNELS);
|
||||
if (n < 0)
|
||||
return;
|
||||
|
||||
in_idx = 0;
|
||||
ch_idx = 0;
|
||||
for (i = 0; i < n; ++i) {
|
||||
pdata->inmode[in_idx][ch_idx] = tmp[i];
|
||||
|
||||
if (++ch_idx == MADERA_MAX_MUXED_CHANNELS) {
|
||||
ch_idx = 0;
|
||||
++in_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void madera_prop_get_pdata(struct madera_priv *priv)
|
||||
{
|
||||
struct madera *madera = priv->madera;
|
||||
struct madera_codec_pdata *pdata = &madera->pdata.codec;
|
||||
u32 out_mono[ARRAY_SIZE(pdata->out_mono)];
|
||||
int i, n;
|
||||
|
||||
madera_prop_get_inmode(priv);
|
||||
|
||||
n = madera_get_variable_u32_array(madera->dev, "cirrus,out-mono",
|
||||
out_mono, ARRAY_SIZE(out_mono), 1);
|
||||
if (n > 0)
|
||||
for (i = 0; i < n; ++i)
|
||||
pdata->out_mono[i] = !!out_mono[i];
|
||||
|
||||
madera_get_variable_u32_array(madera->dev,
|
||||
"cirrus,max-channels-clocked",
|
||||
pdata->max_channels_clocked,
|
||||
ARRAY_SIZE(pdata->max_channels_clocked),
|
||||
1);
|
||||
|
||||
madera_get_variable_u32_array(madera->dev, "cirrus,pdm-fmt",
|
||||
pdata->pdm_fmt,
|
||||
ARRAY_SIZE(pdata->pdm_fmt), 1);
|
||||
|
||||
madera_get_variable_u32_array(madera->dev, "cirrus,pdm-mute",
|
||||
pdata->pdm_mute,
|
||||
ARRAY_SIZE(pdata->pdm_mute), 1);
|
||||
|
||||
madera_get_variable_u32_array(madera->dev, "cirrus,dmic-ref",
|
||||
pdata->dmic_ref,
|
||||
ARRAY_SIZE(pdata->dmic_ref), 1);
|
||||
}
|
||||
|
||||
int madera_core_init(struct madera_priv *priv)
|
||||
{
|
||||
int i;
|
||||
@@ -308,6 +416,9 @@ int madera_core_init(struct madera_priv *priv)
|
||||
BUILD_BUG_ON(!madera_mixer_texts[MADERA_NUM_MIXER_INPUTS - 1]);
|
||||
BUILD_BUG_ON(!madera_mixer_values[MADERA_NUM_MIXER_INPUTS - 1]);
|
||||
|
||||
if (!dev_get_platdata(priv->madera->dev))
|
||||
madera_prop_get_pdata(priv);
|
||||
|
||||
mutex_init(&priv->rate_lock);
|
||||
|
||||
for (i = 0; i < MADERA_MAX_HP_OUTPUT; i++)
|
||||
@@ -944,6 +1055,10 @@ static void madera_configure_input_mode(struct madera *madera)
|
||||
int max_analogue_inputs, max_dmic_sup, i;
|
||||
|
||||
switch (madera->type) {
|
||||
case CS47L15:
|
||||
max_analogue_inputs = 1;
|
||||
max_dmic_sup = 2;
|
||||
break;
|
||||
case CS47L35:
|
||||
max_analogue_inputs = 2;
|
||||
max_dmic_sup = 2;
|
||||
@@ -1770,6 +1885,18 @@ const struct soc_enum madera_asrc1_rate[] = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(madera_asrc1_rate);
|
||||
|
||||
const struct soc_enum madera_asrc1_bidir_rate[] = {
|
||||
SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE1,
|
||||
MADERA_ASRC1_RATE1_SHIFT, 0xf,
|
||||
MADERA_RATE_ENUM_SIZE,
|
||||
madera_rate_text, madera_rate_val),
|
||||
SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE2,
|
||||
MADERA_ASRC1_RATE2_SHIFT, 0xf,
|
||||
MADERA_RATE_ENUM_SIZE,
|
||||
madera_rate_text, madera_rate_val),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(madera_asrc1_bidir_rate);
|
||||
|
||||
const struct soc_enum madera_asrc2_rate[] = {
|
||||
SOC_VALUE_ENUM_SINGLE(MADERA_ASRC2_RATE1,
|
||||
MADERA_ASRC2_RATE1_SHIFT, 0xf,
|
||||
@@ -2149,6 +2276,9 @@ int madera_out_ev(struct snd_soc_dapm_widget *w,
|
||||
switch (madera->type) {
|
||||
case CS47L90:
|
||||
case CS47L91:
|
||||
case CS42L92:
|
||||
case CS47L92:
|
||||
case CS47L93:
|
||||
out_up_delay = 6;
|
||||
break;
|
||||
default:
|
||||
@@ -2264,9 +2394,17 @@ int madera_hp_ev(struct snd_soc_dapm_widget *w,
|
||||
madera->hp_ena &= ~mask;
|
||||
madera->hp_ena |= val;
|
||||
|
||||
/* if OUT1 is routed to EPOUT, ignore HP clamp and impedance */
|
||||
regmap_read(madera->regmap, MADERA_OUTPUT_ENABLES_1, &ep_sel);
|
||||
ep_sel &= MADERA_EP_SEL_MASK;
|
||||
switch (madera->type) {
|
||||
case CS42L92:
|
||||
case CS47L92:
|
||||
case CS47L93:
|
||||
break;
|
||||
default:
|
||||
/* if OUT1 is routed to EPOUT, ignore HP clamp and impedance */
|
||||
regmap_read(madera->regmap, MADERA_OUTPUT_ENABLES_1, &ep_sel);
|
||||
ep_sel &= MADERA_EP_SEL_MASK;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Force off if HPDET has disabled the clamp for this output */
|
||||
if (!ep_sel &&
|
||||
@@ -2442,6 +2580,58 @@ static int madera_get_dspclk_setting(struct madera *madera,
|
||||
}
|
||||
}
|
||||
|
||||
static int madera_set_outclk(struct snd_soc_component *component,
|
||||
unsigned int source, unsigned int freq)
|
||||
{
|
||||
int div, div_inc, rate;
|
||||
|
||||
switch (source) {
|
||||
case MADERA_OUTCLK_SYSCLK:
|
||||
dev_dbg(component->dev, "Configured OUTCLK to SYSCLK\n");
|
||||
snd_soc_component_update_bits(component, MADERA_OUTPUT_RATE_1,
|
||||
MADERA_OUT_CLK_SRC_MASK, source);
|
||||
return 0;
|
||||
case MADERA_OUTCLK_ASYNCCLK:
|
||||
dev_dbg(component->dev, "Configured OUTCLK to ASYNCCLK\n");
|
||||
snd_soc_component_update_bits(component, MADERA_OUTPUT_RATE_1,
|
||||
MADERA_OUT_CLK_SRC_MASK, source);
|
||||
return 0;
|
||||
case MADERA_OUTCLK_MCLK1:
|
||||
case MADERA_OUTCLK_MCLK2:
|
||||
case MADERA_OUTCLK_MCLK3:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (freq % 4000)
|
||||
rate = 5644800;
|
||||
else
|
||||
rate = 6144000;
|
||||
|
||||
div = 1;
|
||||
div_inc = 0;
|
||||
while (div <= 8) {
|
||||
if (freq / div == rate && !(freq % div)) {
|
||||
dev_dbg(component->dev, "Configured %dHz OUTCLK\n", rate);
|
||||
snd_soc_component_update_bits(component,
|
||||
MADERA_OUTPUT_RATE_1,
|
||||
MADERA_OUT_EXT_CLK_DIV_MASK |
|
||||
MADERA_OUT_CLK_SRC_MASK,
|
||||
(div_inc << MADERA_OUT_EXT_CLK_DIV_SHIFT) |
|
||||
source);
|
||||
return 0;
|
||||
}
|
||||
div_inc++;
|
||||
div *= 2;
|
||||
}
|
||||
|
||||
dev_err(component->dev,
|
||||
"Unable to generate %dHz OUTCLK from %dHz MCLK\n",
|
||||
rate, freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int madera_set_sysclk(struct snd_soc_component *component, int clk_id,
|
||||
int source, unsigned int freq, int dir)
|
||||
{
|
||||
@@ -2478,6 +2668,8 @@ int madera_set_sysclk(struct snd_soc_component *component, int clk_id,
|
||||
case MADERA_CLK_OPCLK:
|
||||
case MADERA_CLK_ASYNC_OPCLK:
|
||||
return madera_set_opclk(component, clk_id, freq);
|
||||
case MADERA_CLK_OUTCLK:
|
||||
return madera_set_outclk(component, source, freq);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -2691,6 +2883,10 @@ static const unsigned int madera_sr_vals[] = {
|
||||
#define MADERA_192K_44K1_RATE_MASK 0x003E00
|
||||
#define MADERA_192K_RATE_MASK (MADERA_192K_48K_RATE_MASK | \
|
||||
MADERA_192K_44K1_RATE_MASK)
|
||||
#define MADERA_384K_48K_RATE_MASK 0x0F007E
|
||||
#define MADERA_384K_44K1_RATE_MASK 0x007E00
|
||||
#define MADERA_384K_RATE_MASK (MADERA_384K_48K_RATE_MASK | \
|
||||
MADERA_384K_44K1_RATE_MASK)
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list madera_constraint = {
|
||||
.count = ARRAY_SIZE(madera_sr_vals),
|
||||
@@ -2703,6 +2899,7 @@ static int madera_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
|
||||
struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1];
|
||||
struct madera *madera = priv->madera;
|
||||
unsigned int base_rate;
|
||||
|
||||
if (!substream->runtime)
|
||||
@@ -2722,12 +2919,26 @@ static int madera_startup(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (base_rate == 0)
|
||||
dai_priv->constraint.mask = MADERA_192K_RATE_MASK;
|
||||
else if (base_rate % 4000)
|
||||
dai_priv->constraint.mask = MADERA_192K_44K1_RATE_MASK;
|
||||
else
|
||||
dai_priv->constraint.mask = MADERA_192K_48K_RATE_MASK;
|
||||
switch (madera->type) {
|
||||
case CS42L92:
|
||||
case CS47L92:
|
||||
case CS47L93:
|
||||
if (base_rate == 0)
|
||||
dai_priv->constraint.mask = MADERA_384K_RATE_MASK;
|
||||
else if (base_rate % 4000)
|
||||
dai_priv->constraint.mask = MADERA_384K_44K1_RATE_MASK;
|
||||
else
|
||||
dai_priv->constraint.mask = MADERA_384K_48K_RATE_MASK;
|
||||
break;
|
||||
default:
|
||||
if (base_rate == 0)
|
||||
dai_priv->constraint.mask = MADERA_192K_RATE_MASK;
|
||||
else if (base_rate % 4000)
|
||||
dai_priv->constraint.mask = MADERA_192K_44K1_RATE_MASK;
|
||||
else
|
||||
dai_priv->constraint.mask = MADERA_192K_48K_RATE_MASK;
|
||||
break;
|
||||
}
|
||||
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
@@ -4048,6 +4259,308 @@ int madera_set_fll_ao_refclk(struct madera_fll *fll, int source,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(madera_set_fll_ao_refclk);
|
||||
|
||||
static int madera_fllhj_disable(struct madera_fll *fll)
|
||||
{
|
||||
struct madera *madera = fll->madera;
|
||||
bool change;
|
||||
|
||||
madera_fll_dbg(fll, "Disabling FLL\n");
|
||||
|
||||
/* Disable lockdet, but don't set ctrl_upd update but. This allows the
|
||||
* lock status bit to clear as normal, but should the FLL be enabled
|
||||
* again due to a control clock being required, the lock won't re-assert
|
||||
* as the FLL config registers are automatically applied when the FLL
|
||||
* enables.
|
||||
*/
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_11_OFFS,
|
||||
MADERA_FLL1_LOCKDET_MASK, 0);
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
MADERA_FLL1_HOLD_MASK, MADERA_FLL1_HOLD_MASK);
|
||||
regmap_update_bits_check(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
MADERA_FLL1_ENA_MASK, 0, &change);
|
||||
|
||||
madera_wait_for_fll(fll, false);
|
||||
|
||||
/* ctrl_up gates the writes to all the fll's registers, setting it to 0
|
||||
* here ensures that after a runtime suspend/resume cycle when one
|
||||
* enables the fll then ctrl_up is the last bit that is configured
|
||||
* by the fll enable code rather than the cache sync operation which
|
||||
* would have updated it much earlier before writing out all fll
|
||||
* registers
|
||||
*/
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_2_OFFS,
|
||||
MADERA_FLL1_CTRL_UPD_MASK, 0);
|
||||
|
||||
if (change)
|
||||
pm_runtime_put_autosuspend(madera->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int madera_fllhj_apply(struct madera_fll *fll, int fin)
|
||||
{
|
||||
struct madera *madera = fll->madera;
|
||||
int refdiv, fref, fout, lockdet_thr, fbdiv, hp, fast_clk, fllgcd;
|
||||
bool frac = false;
|
||||
unsigned int fll_n, min_n, max_n, ratio, theta, lambda;
|
||||
unsigned int gains, val, num;
|
||||
|
||||
madera_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout);
|
||||
|
||||
for (refdiv = 0; refdiv < 4; refdiv++)
|
||||
if ((fin / (1 << refdiv)) <= MADERA_FLLHJ_MAX_THRESH)
|
||||
break;
|
||||
|
||||
fref = fin / (1 << refdiv);
|
||||
|
||||
/* Use simple heuristic approach to find a configuration that
|
||||
* should work for most input clocks.
|
||||
*/
|
||||
fast_clk = 0;
|
||||
fout = fll->fout;
|
||||
frac = fout % fref;
|
||||
|
||||
if (fref < MADERA_FLLHJ_LOW_THRESH) {
|
||||
lockdet_thr = 2;
|
||||
gains = MADERA_FLLHJ_LOW_GAINS;
|
||||
if (frac)
|
||||
fbdiv = 256;
|
||||
else
|
||||
fbdiv = 4;
|
||||
} else if (fref < MADERA_FLLHJ_MID_THRESH) {
|
||||
lockdet_thr = 8;
|
||||
gains = MADERA_FLLHJ_MID_GAINS;
|
||||
fbdiv = 1;
|
||||
} else {
|
||||
lockdet_thr = 8;
|
||||
gains = MADERA_FLLHJ_HIGH_GAINS;
|
||||
fbdiv = 1;
|
||||
/* For high speed input clocks, enable 300MHz fast oscillator
|
||||
* when we're in fractional divider mode.
|
||||
*/
|
||||
if (frac) {
|
||||
fast_clk = 0x3;
|
||||
fout = fll->fout * 6;
|
||||
}
|
||||
}
|
||||
/* Use high performance mode for fractional configurations. */
|
||||
if (frac) {
|
||||
hp = 0x3;
|
||||
min_n = MADERA_FLLHJ_FRAC_MIN_N;
|
||||
max_n = MADERA_FLLHJ_FRAC_MAX_N;
|
||||
} else {
|
||||
hp = 0x0;
|
||||
min_n = MADERA_FLLHJ_INT_MIN_N;
|
||||
max_n = MADERA_FLLHJ_INT_MAX_N;
|
||||
}
|
||||
|
||||
ratio = fout / fref;
|
||||
|
||||
madera_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n",
|
||||
refdiv, fref, frac);
|
||||
|
||||
while (ratio / fbdiv < min_n) {
|
||||
fbdiv /= 2;
|
||||
if (fbdiv < 1) {
|
||||
madera_fll_err(fll, "FBDIV (%d) must be >= 1\n", fbdiv);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
while (frac && (ratio / fbdiv > max_n)) {
|
||||
fbdiv *= 2;
|
||||
if (fbdiv >= 1024) {
|
||||
madera_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
madera_fll_dbg(fll, "lockdet=%d, hp=0x%x, fbdiv:%d\n",
|
||||
lockdet_thr, hp, fbdiv);
|
||||
|
||||
/* Calculate N.K values */
|
||||
fllgcd = gcd(fout, fbdiv * fref);
|
||||
num = fout / fllgcd;
|
||||
lambda = (fref * fbdiv) / fllgcd;
|
||||
fll_n = num / lambda;
|
||||
theta = num % lambda;
|
||||
|
||||
madera_fll_dbg(fll, "fll_n=%d, gcd=%d, theta=%d, lambda=%d\n",
|
||||
fll_n, fllgcd, theta, lambda);
|
||||
|
||||
/* Some sanity checks before any registers are written. */
|
||||
if (fll_n < min_n || fll_n > max_n) {
|
||||
madera_fll_err(fll, "N not in valid %s mode range %d-%d: %d\n",
|
||||
frac ? "fractional" : "integer", min_n, max_n,
|
||||
fll_n);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (fbdiv < 1 || (frac && fbdiv >= 1024) || (!frac && fbdiv >= 256)) {
|
||||
madera_fll_err(fll, "Invalid fbdiv for %s mode (%u)\n",
|
||||
frac ? "fractional" : "integer", fbdiv);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* clear the ctrl_upd bit to guarantee we write to it later. */
|
||||
regmap_write(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_2_OFFS,
|
||||
fll_n << MADERA_FLL1_N_SHIFT);
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_3_OFFS,
|
||||
MADERA_FLL1_THETA_MASK,
|
||||
theta << MADERA_FLL1_THETA_SHIFT);
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_4_OFFS,
|
||||
MADERA_FLL1_LAMBDA_MASK,
|
||||
lambda << MADERA_FLL1_LAMBDA_SHIFT);
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_5_OFFS,
|
||||
MADERA_FLL1_FB_DIV_MASK,
|
||||
fbdiv << MADERA_FLL1_FB_DIV_SHIFT);
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_6_OFFS,
|
||||
MADERA_FLL1_REFCLK_DIV_MASK,
|
||||
refdiv << MADERA_FLL1_REFCLK_DIV_SHIFT);
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_GAIN_OFFS,
|
||||
0xffff,
|
||||
gains);
|
||||
val = hp << MADERA_FLL1_HP_SHIFT;
|
||||
val |= 1 << MADERA_FLL1_PHASEDET_ENA_SHIFT;
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_10_OFFS,
|
||||
MADERA_FLL1_HP_MASK | MADERA_FLL1_PHASEDET_ENA_MASK,
|
||||
val);
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_11_OFFS,
|
||||
MADERA_FLL1_LOCKDET_THR_MASK,
|
||||
lockdet_thr << MADERA_FLL1_LOCKDET_THR_SHIFT);
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL1_DIGITAL_TEST_1_OFFS,
|
||||
MADERA_FLL1_SYNC_EFS_ENA_MASK |
|
||||
MADERA_FLL1_CLK_VCO_FAST_SRC_MASK,
|
||||
fast_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int madera_fllhj_enable(struct madera_fll *fll)
|
||||
{
|
||||
struct madera *madera = fll->madera;
|
||||
int already_enabled = madera_is_enabled_fll(fll, fll->base);
|
||||
int ret;
|
||||
|
||||
if (already_enabled < 0)
|
||||
return already_enabled;
|
||||
|
||||
if (!already_enabled)
|
||||
pm_runtime_get_sync(madera->dev);
|
||||
|
||||
madera_fll_dbg(fll, "Enabling FLL, initially %s\n",
|
||||
already_enabled ? "enabled" : "disabled");
|
||||
|
||||
/* FLLn_HOLD must be set before configuring any registers */
|
||||
regmap_update_bits(fll->madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
MADERA_FLL1_HOLD_MASK,
|
||||
MADERA_FLL1_HOLD_MASK);
|
||||
|
||||
/* Apply refclk */
|
||||
ret = madera_fllhj_apply(fll, fll->ref_freq);
|
||||
if (ret) {
|
||||
madera_fll_err(fll, "Failed to set FLL: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
CS47L92_FLL1_REFCLK_SRC_MASK,
|
||||
fll->ref_src << CS47L92_FLL1_REFCLK_SRC_SHIFT);
|
||||
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
MADERA_FLL1_ENA_MASK,
|
||||
MADERA_FLL1_ENA_MASK);
|
||||
|
||||
out:
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_11_OFFS,
|
||||
MADERA_FLL1_LOCKDET_MASK,
|
||||
MADERA_FLL1_LOCKDET_MASK);
|
||||
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_2_OFFS,
|
||||
MADERA_FLL1_CTRL_UPD_MASK,
|
||||
MADERA_FLL1_CTRL_UPD_MASK);
|
||||
|
||||
/* Release the hold so that flln locks to external frequency */
|
||||
regmap_update_bits(madera->regmap,
|
||||
fll->base + MADERA_FLL_CONTROL_1_OFFS,
|
||||
MADERA_FLL1_HOLD_MASK,
|
||||
0);
|
||||
|
||||
if (!already_enabled)
|
||||
madera_wait_for_fll(fll, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int madera_fllhj_validate(struct madera_fll *fll,
|
||||
unsigned int ref_in,
|
||||
unsigned int fout)
|
||||
{
|
||||
if (fout && !ref_in) {
|
||||
madera_fll_err(fll, "fllout set without valid input clk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fll->fout && fout != fll->fout) {
|
||||
madera_fll_err(fll, "Can't change output on active FLL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ref_in / MADERA_FLL_MAX_REFDIV > MADERA_FLLHJ_MAX_THRESH) {
|
||||
madera_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int madera_fllhj_set_refclk(struct madera_fll *fll, int source,
|
||||
unsigned int fin, unsigned int fout)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* To remain consistent with previous FLLs, we expect fout to be
|
||||
* provided in the form of the required sysclk rate, which is
|
||||
* 2x the calculated fll out.
|
||||
*/
|
||||
if (fout)
|
||||
fout /= 2;
|
||||
|
||||
if (fll->ref_src == source && fll->ref_freq == fin &&
|
||||
fll->fout == fout)
|
||||
return 0;
|
||||
|
||||
if (fin && fout && madera_fllhj_validate(fll, fin, fout))
|
||||
return -EINVAL;
|
||||
|
||||
fll->ref_src = source;
|
||||
fll->ref_freq = fin;
|
||||
fll->fout = fout;
|
||||
|
||||
if (fout)
|
||||
ret = madera_fllhj_enable(fll);
|
||||
else
|
||||
madera_fllhj_disable(fll);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(madera_fllhj_set_refclk);
|
||||
|
||||
/**
|
||||
* madera_set_output_mode - Set the mode of the specified output
|
||||
*
|
||||
|
@@ -47,6 +47,7 @@
|
||||
#define MADERA_CLK_SYSCLK_3 6
|
||||
#define MADERA_CLK_ASYNCCLK_2 7
|
||||
#define MADERA_CLK_DSPCLK 8
|
||||
#define MADERA_CLK_OUTCLK 9
|
||||
|
||||
#define MADERA_CLK_SRC_MCLK1 0x0
|
||||
#define MADERA_CLK_SRC_MCLK2 0x1
|
||||
@@ -61,6 +62,12 @@
|
||||
#define MADERA_CLK_SRC_AIF4BCLK 0xB
|
||||
#define MADERA_CLK_SRC_FLLAO 0xF
|
||||
|
||||
#define MADERA_OUTCLK_SYSCLK 0
|
||||
#define MADERA_OUTCLK_ASYNCCLK 1
|
||||
#define MADERA_OUTCLK_MCLK1 4
|
||||
#define MADERA_OUTCLK_MCLK2 5
|
||||
#define MADERA_OUTCLK_MCLK3 6
|
||||
|
||||
#define MADERA_MIXER_VOL_MASK 0x00FE
|
||||
#define MADERA_MIXER_VOL_SHIFT 1
|
||||
#define MADERA_MIXER_VOL_WIDTH 7
|
||||
@@ -326,6 +333,7 @@ extern const struct soc_enum madera_sample_rate[];
|
||||
extern const struct soc_enum madera_isrc_fsl[];
|
||||
extern const struct soc_enum madera_isrc_fsh[];
|
||||
extern const struct soc_enum madera_asrc1_rate[];
|
||||
extern const struct soc_enum madera_asrc1_bidir_rate[];
|
||||
extern const struct soc_enum madera_asrc2_rate[];
|
||||
extern const struct soc_enum madera_dfc_width[];
|
||||
extern const struct soc_enum madera_dfc_type[];
|
||||
@@ -403,6 +411,8 @@ int madera_set_fll_syncclk(struct madera_fll *fll, int source,
|
||||
unsigned int fref, unsigned int fout);
|
||||
int madera_set_fll_ao_refclk(struct madera_fll *fll, int source,
|
||||
unsigned int fin, unsigned int fout);
|
||||
int madera_fllhj_set_refclk(struct madera_fll *fll, int source,
|
||||
unsigned int fin, unsigned int fout);
|
||||
|
||||
int madera_core_init(struct madera_priv *priv);
|
||||
int madera_core_free(struct madera_priv *priv);
|
||||
|
@@ -154,10 +154,6 @@ static const DECLARE_TLV_DB_RANGE(max98371_gain_tlv,
|
||||
8, 10, TLV_DB_SCALE_ITEM(400, 100, 0)
|
||||
);
|
||||
|
||||
static const DECLARE_TLV_DB_RANGE(max98371_noload_gain_tlv,
|
||||
0, 11, TLV_DB_SCALE_ITEM(950, 100, 0),
|
||||
);
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(digital_tlv, -6300, 50, 1);
|
||||
|
||||
static const struct snd_kcontrol_new max98371_snd_controls[] = {
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "max98373.h"
|
||||
@@ -901,6 +902,17 @@ static void max98373_slot_config(struct i2c_client *i2c,
|
||||
else
|
||||
max98373->i_slot = 1;
|
||||
|
||||
max98373->reset_gpio = of_get_named_gpio(dev->of_node,
|
||||
"maxim,reset-gpio", 0);
|
||||
if (!gpio_is_valid(max98373->reset_gpio)) {
|
||||
dev_err(dev, "Looking up %s property in node %s failed %d\n",
|
||||
"maxim,reset-gpio", dev->of_node->full_name,
|
||||
max98373->reset_gpio);
|
||||
} else {
|
||||
dev_dbg(dev, "maxim,reset-gpio=%d",
|
||||
max98373->reset_gpio);
|
||||
}
|
||||
|
||||
if (!device_property_read_u32(dev, "maxim,spkfb-slot-no", &value))
|
||||
max98373->spkfb_slot = value & 0xF;
|
||||
else
|
||||
@@ -929,7 +941,6 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
|
||||
else
|
||||
max98373->interleave_mode = false;
|
||||
|
||||
|
||||
/* regmap initialization */
|
||||
max98373->regmap
|
||||
= devm_regmap_init_i2c(i2c, &max98373_regmap);
|
||||
@@ -940,6 +951,24 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* voltage/current slot & gpio configuration */
|
||||
max98373_slot_config(i2c, max98373);
|
||||
|
||||
/* Power on device */
|
||||
if (gpio_is_valid(max98373->reset_gpio)) {
|
||||
ret = gpio_request(max98373->reset_gpio, "MAX98373_RESET");
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "%s: Failed to request gpio %d\n",
|
||||
__func__, max98373->reset_gpio);
|
||||
gpio_free(max98373->reset_gpio);
|
||||
return -EINVAL;
|
||||
}
|
||||
gpio_direction_output(max98373->reset_gpio, 0);
|
||||
msleep(50);
|
||||
gpio_direction_output(max98373->reset_gpio, 1);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
/* Check Revision ID */
|
||||
ret = regmap_read(max98373->regmap,
|
||||
MAX98373_R21FF_REV_ID, ®);
|
||||
@@ -950,9 +979,6 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
|
||||
}
|
||||
dev_info(&i2c->dev, "MAX98373 revisionID: 0x%02X\n", reg);
|
||||
|
||||
/* voltage/current slot configuration */
|
||||
max98373_slot_config(i2c, max98373);
|
||||
|
||||
/* codec registeration */
|
||||
ret = devm_snd_soc_register_component(&i2c->dev, &soc_codec_dev_max98373,
|
||||
max98373_dai, ARRAY_SIZE(max98373_dai));
|
||||
|
@@ -205,6 +205,7 @@
|
||||
|
||||
struct max98373_priv {
|
||||
struct regmap *regmap;
|
||||
int reset_gpio;
|
||||
unsigned int v_slot;
|
||||
unsigned int i_slot;
|
||||
unsigned int spkfb_slot;
|
||||
|
Деякі з файлів не показано, оскільки було змінено дуже багато файлів Показати більше
Посилання в новій задачі
Заблокувати користувача