Skip to content

Commit 9be0b3a

Browse files
committed
ASoC: SOF: Intel: hda-mlink: fixes and extensions
Merge series from Pierre-Louis Bossart <[email protected]>: With additional testing with multiple links and multiple DAI types, we found a couple of mistakes with refcounts, base address, missing initialization. A new helper was also added due to a change in the SoundWire programming sequences, with the host driver in charge of setting up the DMA channel mapping instead of the firmware.
2 parents 1c0d023 + ccc2f0c commit 9be0b3a

File tree

2 files changed

+101
-9
lines changed

2 files changed

+101
-9
lines changed

include/sound/hda-mlink.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,17 @@ int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink);
4444

4545
int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num);
4646

47+
int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
48+
int channel_mask, int stream_id, int dir);
49+
4750
void hda_bus_ml_put_all(struct hdac_bus *bus);
4851
void hda_bus_ml_reset_losidv(struct hdac_bus *bus);
4952
int hda_bus_ml_resume(struct hdac_bus *bus);
5053
int hda_bus_ml_suspend(struct hdac_bus *bus);
5154

5255
struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus);
5356
struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus);
57+
struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus);
5458

5559
struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid);
5660

@@ -144,6 +148,13 @@ hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) { return
144148
static inline int
145149
hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) { return 0; }
146150

151+
static inline int
152+
hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
153+
int channel_mask, int stream_id, int dir)
154+
{
155+
return 0;
156+
}
157+
147158
static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { }
148159
static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { }
149160
static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; }
@@ -155,6 +166,9 @@ hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; }
155166
static inline struct hdac_ext_link *
156167
hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) { return NULL; }
157168

169+
static inline struct hdac_ext_link *
170+
hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus) { return NULL; }
171+
158172
static inline struct mutex *
159173
hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) { return NULL; }
160174

sound/soc/sof/intel/hda-mlink.c

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919

2020
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
2121

22+
/* worst-case number of sublinks is used for sublink refcount array allocation only */
23+
#define HDAML_MAX_SUBLINKS (AZX_ML_LCTL_CPA_SHIFT - AZX_ML_LCTL_SPA_SHIFT)
24+
2225
/**
2326
* struct hdac_ext2_link - HDAudio extended+alternate link
2427
*
@@ -33,6 +36,7 @@
3336
* @leptr: extended link pointer
3437
* @eml_lock: mutual exclusion to access shared registers e.g. CPA/SPA bits
3538
* in LCTL register
39+
* @sublink_ref_count: array of refcounts, required to power-manage sublinks independently
3640
* @base_ptr: pointer to shim/ip/shim_vs space
3741
* @instance_offset: offset between each of @slcount instances managed by link
3842
* @shim_offset: offset to SHIM register base
@@ -53,6 +57,7 @@ struct hdac_ext2_link {
5357
u32 leptr;
5458

5559
struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
60+
int sublink_ref_count[HDAML_MAX_SUBLINKS];
5661

5762
/* internal values computed from LCAP contents */
5863
void __iomem *base_ptr;
@@ -68,6 +73,7 @@ struct hdac_ext2_link {
6873
#define AZX_REG_SDW_SHIM_OFFSET 0x0
6974
#define AZX_REG_SDW_IP_OFFSET 0x100
7075
#define AZX_REG_SDW_VS_SHIM_OFFSET 0x6000
76+
#define AZX_REG_SDW_SHIM_PCMSyCM(y) (0x16 + 0x4 * (y))
7177

7278
/* only one instance supported */
7379
#define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0
@@ -91,7 +97,7 @@ struct hdac_ext2_link {
9197
*/
9298

9399
static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
94-
void __iomem *ml_addr, int link_idx)
100+
void __iomem *remap_addr, void __iomem *ml_addr, int link_idx)
95101
{
96102
struct hdac_ext_link *hlink = &h2link->hext_link;
97103
u32 base_offset;
@@ -126,15 +132,16 @@ static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
126132
link_idx, h2link->slcount);
127133

128134
/* find IP ID and offsets */
129-
h2link->leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
135+
h2link->leptr = readl(ml_addr + AZX_REG_ML_LEPTR);
130136

131137
h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
132138

133139
base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
134-
h2link->base_ptr = hlink->ml_addr + base_offset;
140+
h2link->base_ptr = remap_addr + base_offset;
135141

136142
switch (h2link->elid) {
137143
case AZX_REG_ML_LEPTR_ID_SDW:
144+
h2link->instance_offset = AZX_REG_SDW_INSTANCE_OFFSET;
138145
h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
139146
h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
140147
h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
@@ -149,6 +156,7 @@ static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
149156
link_idx, base_offset);
150157
break;
151158
case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
159+
h2link->instance_offset = AZX_REG_INTEL_SSP_INSTANCE_OFFSET;
152160
h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
153161
h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
154162
h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
@@ -333,6 +341,21 @@ static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num)
333341
writel(val, lsdiid);
334342
}
335343

344+
static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan,
345+
int stream_id, int dir)
346+
{
347+
u16 val;
348+
349+
val = readw(pcmsycm);
350+
351+
u16p_replace_bits(&val, lchan, GENMASK(3, 0));
352+
u16p_replace_bits(&val, hchan, GENMASK(7, 4));
353+
u16p_replace_bits(&val, stream_id, GENMASK(13, 8));
354+
u16p_replace_bits(&val, dir, BIT(15));
355+
356+
writew(val, pcmsycm);
357+
}
358+
336359
static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
337360
{
338361
u32 val = readl(lctl);
@@ -364,7 +387,7 @@ static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
364387
hlink->bus = bus;
365388
hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
366389

367-
ret = hdaml_lnk_enum(bus->dev, h2link, hlink->ml_addr, index);
390+
ret = hdaml_lnk_enum(bus->dev, h2link, bus->remap_addr, hlink->ml_addr, index);
368391
if (ret < 0) {
369392
kfree(h2link);
370393
return ret;
@@ -641,8 +664,13 @@ static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid,
641664
if (eml_lock)
642665
mutex_lock(&h2link->eml_lock);
643666

644-
if (++hlink->ref_count > 1)
645-
goto skip_init;
667+
if (!alt) {
668+
if (++hlink->ref_count > 1)
669+
goto skip_init;
670+
} else {
671+
if (++h2link->sublink_ref_count[sublink] > 1)
672+
goto skip_init;
673+
}
646674

647675
ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
648676

@@ -684,9 +712,13 @@ static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid
684712
if (eml_lock)
685713
mutex_lock(&h2link->eml_lock);
686714

687-
if (--hlink->ref_count > 0)
688-
goto skip_shutdown;
689-
715+
if (!alt) {
716+
if (--hlink->ref_count > 0)
717+
goto skip_shutdown;
718+
} else {
719+
if (--h2link->sublink_ref_count[sublink] > 0)
720+
goto skip_shutdown;
721+
}
690722
ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
691723

692724
skip_shutdown:
@@ -740,6 +772,40 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
740772
return 0;
741773
} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK);
742774

775+
/*
776+
* the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the
777+
* PDI index, i.e. the FIFO used for RX or TX
778+
*/
779+
int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
780+
int channel_mask, int stream_id, int dir)
781+
{
782+
struct hdac_ext2_link *h2link;
783+
u16 __iomem *pcmsycm;
784+
u16 val;
785+
786+
h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
787+
if (!h2link)
788+
return -ENODEV;
789+
790+
pcmsycm = h2link->base_ptr + h2link->shim_offset +
791+
h2link->instance_offset * sublink +
792+
AZX_REG_SDW_SHIM_PCMSyCM(y);
793+
794+
mutex_lock(&h2link->eml_lock);
795+
796+
hdaml_shim_map_stream_ch(pcmsycm, 0, hweight32(channel_mask),
797+
stream_id, dir);
798+
799+
mutex_unlock(&h2link->eml_lock);
800+
801+
val = readw(pcmsycm);
802+
803+
dev_dbg(bus->dev, "channel_mask %#x stream_id %d dir %d pcmscm %#x\n",
804+
channel_mask, stream_id, dir, val);
805+
806+
return 0;
807+
} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, SND_SOC_SOF_HDA_MLINK);
808+
743809
void hda_bus_ml_put_all(struct hdac_bus *bus)
744810
{
745811
struct hdac_ext_link *hlink;
@@ -836,6 +902,18 @@ struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
836902
}
837903
EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);
838904

905+
struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus)
906+
{
907+
struct hdac_ext2_link *h2link;
908+
909+
h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
910+
if (!h2link)
911+
return NULL;
912+
913+
return &h2link->hext_link;
914+
}
915+
EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, SND_SOC_SOF_HDA_MLINK);
916+
839917
int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
840918
{
841919
struct hdac_ext2_link *h2link;

0 commit comments

Comments
 (0)