19
19
20
20
#if IS_ENABLED (CONFIG_SND_SOC_SOF_HDA_MLINK )
21
21
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
+
22
25
/**
23
26
* struct hdac_ext2_link - HDAudio extended+alternate link
24
27
*
33
36
* @leptr: extended link pointer
34
37
* @eml_lock: mutual exclusion to access shared registers e.g. CPA/SPA bits
35
38
* in LCTL register
39
+ * @sublink_ref_count: array of refcounts, required to power-manage sublinks independently
36
40
* @base_ptr: pointer to shim/ip/shim_vs space
37
41
* @instance_offset: offset between each of @slcount instances managed by link
38
42
* @shim_offset: offset to SHIM register base
@@ -53,6 +57,7 @@ struct hdac_ext2_link {
53
57
u32 leptr ;
54
58
55
59
struct mutex eml_lock ; /* prevent concurrent access to e.g. CPA/SPA */
60
+ int sublink_ref_count [HDAML_MAX_SUBLINKS ];
56
61
57
62
/* internal values computed from LCAP contents */
58
63
void __iomem * base_ptr ;
@@ -68,6 +73,7 @@ struct hdac_ext2_link {
68
73
#define AZX_REG_SDW_SHIM_OFFSET 0x0
69
74
#define AZX_REG_SDW_IP_OFFSET 0x100
70
75
#define AZX_REG_SDW_VS_SHIM_OFFSET 0x6000
76
+ #define AZX_REG_SDW_SHIM_PCMSyCM (y ) (0x16 + 0x4 * (y))
71
77
72
78
/* only one instance supported */
73
79
#define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0
@@ -91,7 +97,7 @@ struct hdac_ext2_link {
91
97
*/
92
98
93
99
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 )
95
101
{
96
102
struct hdac_ext_link * hlink = & h2link -> hext_link ;
97
103
u32 base_offset ;
@@ -126,15 +132,16 @@ static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
126
132
link_idx , h2link -> slcount );
127
133
128
134
/* 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 );
130
136
131
137
h2link -> elid = FIELD_GET (AZX_REG_ML_LEPTR_ID , h2link -> leptr );
132
138
133
139
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 ;
135
141
136
142
switch (h2link -> elid ) {
137
143
case AZX_REG_ML_LEPTR_ID_SDW :
144
+ h2link -> instance_offset = AZX_REG_SDW_INSTANCE_OFFSET ;
138
145
h2link -> shim_offset = AZX_REG_SDW_SHIM_OFFSET ;
139
146
h2link -> ip_offset = AZX_REG_SDW_IP_OFFSET ;
140
147
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,
149
156
link_idx , base_offset );
150
157
break ;
151
158
case AZX_REG_ML_LEPTR_ID_INTEL_SSP :
159
+ h2link -> instance_offset = AZX_REG_INTEL_SSP_INSTANCE_OFFSET ;
152
160
h2link -> shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET ;
153
161
h2link -> ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET ;
154
162
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)
333
341
writel (val , lsdiid );
334
342
}
335
343
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
+
336
359
static void hdaml_lctl_offload_enable (u32 __iomem * lctl , bool enable )
337
360
{
338
361
u32 val = readl (lctl );
@@ -364,7 +387,7 @@ static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
364
387
hlink -> bus = bus ;
365
388
hlink -> ml_addr = bus -> mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index );
366
389
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 );
368
391
if (ret < 0 ) {
369
392
kfree (h2link );
370
393
return ret ;
@@ -641,8 +664,13 @@ static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid,
641
664
if (eml_lock )
642
665
mutex_lock (& h2link -> eml_lock );
643
666
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
+ }
646
674
647
675
ret = hdaml_link_init (hlink -> ml_addr + AZX_REG_ML_LCTL , sublink );
648
676
@@ -684,9 +712,13 @@ static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid
684
712
if (eml_lock )
685
713
mutex_lock (& h2link -> eml_lock );
686
714
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
+ }
690
722
ret = hdaml_link_shutdown (hlink -> ml_addr + AZX_REG_ML_LCTL , sublink );
691
723
692
724
skip_shutdown :
@@ -740,6 +772,40 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
740
772
return 0 ;
741
773
} EXPORT_SYMBOL_NS (hdac_bus_eml_sdw_set_lsdiid , SND_SOC_SOF_HDA_MLINK );
742
774
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
+
743
809
void hda_bus_ml_put_all (struct hdac_bus * bus )
744
810
{
745
811
struct hdac_ext_link * hlink ;
@@ -836,6 +902,18 @@ struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
836
902
}
837
903
EXPORT_SYMBOL_NS (hdac_bus_eml_dmic_get_hlink , SND_SOC_SOF_HDA_MLINK );
838
904
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
+
839
917
int hdac_bus_eml_enable_offload (struct hdac_bus * bus , bool alt , int elid , bool enable )
840
918
{
841
919
struct hdac_ext2_link * h2link ;
0 commit comments