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 *
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
9399static 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+
336359static 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
692724skip_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+
743809void 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}
837903EXPORT_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+
839917int 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