Message ID | 20230712091048.2545319-5-cezary.rojewski@intel.com |
---|---|
State | Superseded |
Headers | show |
Series | ACPI: NHLT: Access and query helpers | expand |
On 2023-07-12 5:48 PM, Andy Shevchenko wrote: > On Wed, Jul 12, 2023 at 11:10:48AM +0200, Cezary Rojewski wrote: ... >> +bool acpi_nhlt_endpoint_match(const struct acpi_nhlt_endpoint *ep, >> + int link_type, int dev_type, int dir, int bus_id) >> +{ >> + return ep && >> + (link_type < 0 || ep->link_type == link_type) && >> + (dev_type < 0 || ep->device_type == dev_type) && >> + (dir < 0 || ep->direction == dir) && >> + (bus_id < 0 || ep->virtual_bus_id == bus_id); > > I think you can split these for better reading. > > if (!ep) > return false; > > if (link_type >= 0 && ep->link_type != link_type) > return false; > > if (dev_type >= 0 && ep->device_type != dev_type) > return false; > > if (dir >= 0 && ep->direction != dir) > return false; > > if (bus_id >= 0 && ep->virtual_bus_id != bus_id) > return false; > > return true; > > Yes, much more lines, but readability is better in my opinion. > I also believe that code generation on x86_64 will be the same. While I favor readability over less lines of code, I do not think splitting the conditions makes it easier in this case. Perhaps reverse-christmas-tree? Pivoted around '<'. return ep && (link_type < 0 || ep->link_type == link_type) && (dev_type < 0 || ep->device_type == dev_type) && (bus_id < 0 || ep->virtual_bus_id == bus_id) && (dir < 0 || ep->direction == dir); In general I'd like to distinguish between conditions that one _has to_ read and understand and those which reader _may_ pass by. Here, we are checking description of an endpoint for equality. Nothing more, nothing less. >> +} ... >> +struct acpi_nhlt_endpoint * >> +acpi_nhlt_find_endpoint(const struct acpi_table_nhlt *tb, >> + int link_type, int dev_type, int dir, int bus_id) >> +{ >> + struct acpi_nhlt_endpoint *ep; > >> + if (!tb) >> + return ERR_PTR(-EINVAL); > > Just wondering how often we will have a caller that supplies NULL for tb. Depends on kernel's philosophy on public API. In general, public API should defend themselves from harmful and illegal callers. However, in low level areas 'illegal' is sometimes mistaken with illogical. In such cases double safety can be dropped. Or, perhaps you were discussing return value here and ERR_PTR(-EINVAL) could be replaced by something else or even NULL. >> + for_each_nhlt_endpoint(tb, ep) >> + if (acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id)) >> + return ep; >> + return NULL; >> +} > > ... > >> +struct acpi_nhlt_format_config * >> +acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt_endpoint *ep, >> + u16 ch, u32 rate, u16 vbps, u16 bps) >> +{ >> + struct acpi_nhlt_wave_extensible *wav; >> + struct acpi_nhlt_format_config *fmt; > >> + if (!ep) >> + return ERR_PTR(-EINVAL); > > Similar Q as above. > >> + for_each_nhlt_endpoint_fmtcfg(ep, fmt) { >> + wav = &fmt->format; >> + >> + if (wav->channel_count == ch && >> + wav->valid_bits_per_sample == vbps && >> + wav->bits_per_sample == bps && >> + wav->samples_per_sec == rate) > > Also can be split, but this one readable in the original form. As order does not really matter here, perhaps reverse-christmas-tree to improve readability? if (wav->valid_bits_per_sample == vpbs && wav->samples_per_sec == rate && wav->bits_per_sample == bps && wav->channel_count == ch) >> + return fmt; >> + } >> + >> + return NULL; >> +} ... >> +struct acpi_nhlt_format_config * >> +acpi_nhlt_find_fmtcfg(const struct acpi_table_nhlt *tb, >> + int link_type, int dev_type, int dir, int bus_id, >> + u16 ch, u32 rate, u16 vbps, u16 bps) >> +{ >> + struct acpi_nhlt_format_config *fmt; >> + struct acpi_nhlt_endpoint *ep; > >> + if (!tb) >> + return ERR_PTR(-EINVAL); > > Same as above. > Looking at them, wouldn't simply returning NULL suffice? > >> + for_each_nhlt_endpoint(tb, ep) { >> + if (!acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id)) >> + continue; >> + >> + fmt = acpi_nhlt_endpoint_find_fmtcfg(ep, ch, rate, vbps, bps); >> + if (fmt) >> + return fmt; >> + } >> + >> + return NULL; >> +} > > ... > >> +int acpi_nhlt_endpoint_dmic_count(const struct acpi_nhlt_endpoint *ep) >> +{ >> + struct acpi_nhlt_vendor_mic_devcfg *vendor_cfg; >> + struct acpi_nhlt_format_config *fmt; >> + struct acpi_nhlt_mic_devcfg *devcfg; >> + u16 max_ch = 0; >> + >> + if (!ep || ep->link_type != ACPI_NHLT_PDM) >> + return -EINVAL; >> + >> + /* Find max number of channels based on formats configuration. */ >> + for_each_nhlt_endpoint_fmtcfg(ep, fmt) >> + max_ch = max(fmt->format.channel_count, max_ch); >> + >> + /* If @ep not a mic array, fallback to channels count. */ >> + devcfg = acpi_nhlt_endpoint_mic_devcfg(ep); >> + if (!devcfg || devcfg->config_type != ACPI_NHLT_CONFIG_TYPE_MIC_ARRAY) >> + return max_ch; >> + >> + switch (devcfg->array_type) { >> + case ACPI_NHLT_SMALL_LINEAR_2ELEMENT: >> + case ACPI_NHLT_BIG_LINEAR_2ELEMENT: >> + return 2; >> + >> + case ACPI_NHLT_FIRST_GEOMETRY_LINEAR_4ELEMENT: >> + case ACPI_NHLT_PLANAR_LSHAPED_4ELEMENT: >> + case ACPI_NHLT_SECOND_GEOMETRY_LINEAR_4ELEMENT: >> + return 4; >> + >> + case ACPI_NHLT_VENDOR_DEFINED: >> + vendor_cfg = acpi_nhlt_endpoint_vendor_mic_devcfg(ep); >> + if (!vendor_cfg) >> + return -EINVAL; >> + return vendor_cfg->num_mics; >> + >> + default: > >> + pr_warn("undefined mic array type: %#x\n", devcfg->array_type); > > Is this function can ever be called without backing device? If not, > I would supply (ACPI?) device pointer and use dev_warn() instead. > > But I'm not sure about this. Up to you, folks. Given what's our there in the market I wouldn't say it's impossible to encounter such scenario. Could you elaborate on what you meant by "supply device pointer"? >> + return max_ch; >> + } >> +} >
On Mon, Jul 17, 2023 at 10:29:17AM +0200, Cezary Rojewski wrote: > On 2023-07-12 5:48 PM, Andy Shevchenko wrote: > > On Wed, Jul 12, 2023 at 11:10:48AM +0200, Cezary Rojewski wrote: ... > > > + return ep && > > > + (link_type < 0 || ep->link_type == link_type) && > > > + (dev_type < 0 || ep->device_type == dev_type) && > > > + (dir < 0 || ep->direction == dir) && > > > + (bus_id < 0 || ep->virtual_bus_id == bus_id); > > > > I think you can split these for better reading. > > > > if (!ep) > > return false; > > > > if (link_type >= 0 && ep->link_type != link_type) > > return false; > > > > if (dev_type >= 0 && ep->device_type != dev_type) > > return false; > > > > if (dir >= 0 && ep->direction != dir) > > return false; > > > > if (bus_id >= 0 && ep->virtual_bus_id != bus_id) > > return false; > > > > return true; > > > > Yes, much more lines, but readability is better in my opinion. > > I also believe that code generation on x86_64 will be the same. > > While I favor readability over less lines of code, I do not think splitting > the conditions makes it easier in this case. Perhaps reverse-christmas-tree? > Pivoted around '<'. > > return ep && > (link_type < 0 || ep->link_type == link_type) && > (dev_type < 0 || ep->device_type == dev_type) && > (bus_id < 0 || ep->virtual_bus_id == bus_id) && > (dir < 0 || ep->direction == dir); This one definitely better. > In general I'd like to distinguish between conditions that one _has to_ read > and understand and those which reader _may_ pass by. Here, we are checking > description of an endpoint for equality. Nothing more, nothing less. ... > > > +struct acpi_nhlt_endpoint * > > > +acpi_nhlt_find_endpoint(const struct acpi_table_nhlt *tb, > > > + int link_type, int dev_type, int dir, int bus_id) > > > +{ > > > + struct acpi_nhlt_endpoint *ep; > > > > > + if (!tb) > > > + return ERR_PTR(-EINVAL); > > > > Just wondering how often we will have a caller that supplies NULL for tb. > > Depends on kernel's philosophy on public API. In general, public API should > defend themselves from harmful and illegal callers. However, in low level > areas 'illegal' is sometimes mistaken with illogical. In such cases double > safety can be dropped. What do you put under "public"? ABI? Or just internally available API? If the latter, we don't do defensive programming there, we usually just add NULL(invalid data)-awareness to the free()-like functions. > Or, perhaps you were discussing return value here and ERR_PTR(-EINVAL) could > be replaced by something else or even NULL. I prefer to get rid of those. > > > + for_each_nhlt_endpoint(tb, ep) > > > + if (acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id)) > > > + return ep; > > > + return NULL; > > > +} ... > > > + if (wav->channel_count == ch && > > > + wav->valid_bits_per_sample == vbps && > > > + wav->bits_per_sample == bps && > > > + wav->samples_per_sec == rate) > > > > Also can be split, but this one readable in the original form. > > As order does not really matter here, perhaps reverse-christmas-tree to > improve readability? > > if (wav->valid_bits_per_sample == vpbs && > wav->samples_per_sec == rate && > wav->bits_per_sample == bps && > wav->channel_count == ch) OK! > > > + return fmt; ... > > > + default: > > > > > + pr_warn("undefined mic array type: %#x\n", devcfg->array_type); > > > > Is this function can ever be called without backing device? If not, > > I would supply (ACPI?) device pointer and use dev_warn() instead. > > > > But I'm not sure about this. Up to you, folks. > > Given what's our there in the market I wouldn't say it's impossible to > encounter such scenario. Could you elaborate on what you meant by "supply > device pointer"? In the caller (assuming it has ACPI device): ret = acpi_nhlt_endpoint_dmic_count(adev, ep); if (ret < 0) ... > > > + return max_ch; > > > + }
On 2023-07-17 11:47 AM, Andy Shevchenko wrote: > On Mon, Jul 17, 2023 at 10:29:17AM +0200, Cezary Rojewski wrote: >> On 2023-07-12 5:48 PM, Andy Shevchenko wrote: >>> On Wed, Jul 12, 2023 at 11:10:48AM +0200, Cezary Rojewski wrote: ... >>>> + return ep && >>>> + (link_type < 0 || ep->link_type == link_type) && >>>> + (dev_type < 0 || ep->device_type == dev_type) && >>>> + (dir < 0 || ep->direction == dir) && >>>> + (bus_id < 0 || ep->virtual_bus_id == bus_id); >>> >>> I think you can split these for better reading. >>> >>> if (!ep) >>> return false; >>> >>> if (link_type >= 0 && ep->link_type != link_type) >>> return false; >>> >>> if (dev_type >= 0 && ep->device_type != dev_type) >>> return false; >>> >>> if (dir >= 0 && ep->direction != dir) >>> return false; >>> >>> if (bus_id >= 0 && ep->virtual_bus_id != bus_id) >>> return false; >>> >>> return true; >>> >>> Yes, much more lines, but readability is better in my opinion. >>> I also believe that code generation on x86_64 will be the same. >> >> While I favor readability over less lines of code, I do not think splitting >> the conditions makes it easier in this case. Perhaps reverse-christmas-tree? >> Pivoted around '<'. >> >> return ep && >> (link_type < 0 || ep->link_type == link_type) && >> (dev_type < 0 || ep->device_type == dev_type) && >> (bus_id < 0 || ep->virtual_bus_id == bus_id) && >> (dir < 0 || ep->direction == dir); > > This one definitely better. Ack. >> In general I'd like to distinguish between conditions that one _has to_ read >> and understand and those which reader _may_ pass by. Here, we are checking >> description of an endpoint for equality. Nothing more, nothing less. > > ... > >>>> +struct acpi_nhlt_endpoint * >>>> +acpi_nhlt_find_endpoint(const struct acpi_table_nhlt *tb, >>>> + int link_type, int dev_type, int dir, int bus_id) >>>> +{ >>>> + struct acpi_nhlt_endpoint *ep; >>> >>>> + if (!tb) >>>> + return ERR_PTR(-EINVAL); >>> >>> Just wondering how often we will have a caller that supplies NULL for tb. >> >> Depends on kernel's philosophy on public API. In general, public API should >> defend themselves from harmful and illegal callers. However, in low level >> areas 'illegal' is sometimes mistaken with illogical. In such cases double >> safety can be dropped. > > What do you put under "public"? ABI? Or just internally available API? > If the latter, we don't do defensive programming there, we usually just > add NULL(invalid data)-awareness to the free()-like functions. Thanks for explaining! >> Or, perhaps you were discussing return value here and ERR_PTR(-EINVAL) could >> be replaced by something else or even NULL. > > I prefer to get rid of those. Agreed. >>>> + for_each_nhlt_endpoint(tb, ep) >>>> + if (acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id)) >>>> + return ep; >>>> + return NULL; >>>> +} > > ... > >>>> + if (wav->channel_count == ch && >>>> + wav->valid_bits_per_sample == vbps && >>>> + wav->bits_per_sample == bps && >>>> + wav->samples_per_sec == rate) >>> >>> Also can be split, but this one readable in the original form. >> >> As order does not really matter here, perhaps reverse-christmas-tree to >> improve readability? >> >> if (wav->valid_bits_per_sample == vpbs && >> wav->samples_per_sec == rate && >> wav->bits_per_sample == bps && >> wav->channel_count == ch) > > OK! Ack. >>>> + return fmt; > > ... > >>>> + default: >>> >>>> + pr_warn("undefined mic array type: %#x\n", devcfg->array_type); >>> >>> Is this function can ever be called without backing device? If not, >>> I would supply (ACPI?) device pointer and use dev_warn() instead. >>> >>> But I'm not sure about this. Up to you, folks. >> >> Given what's our there in the market I wouldn't say it's impossible to >> encounter such scenario. Could you elaborate on what you meant by "supply >> device pointer"? > > In the caller (assuming it has ACPI device): > > ret = acpi_nhlt_endpoint_dmic_count(adev, ep); > if (ret < 0) > ... > Thanks for explaining. I'm going to kindly disagree here - dev pointer would be here only to print the warning. NHLT is also independent of any device - even if its present in the system, one may not have a single device that utilizes it. Worth mentioning is fact that all other functions do not accept such argument. Doing so here alone would break API consistency. >>>> + return max_ch; >>>> + } >
On 2023-07-17 11:47 AM, Andy Shevchenko wrote: > On Mon, Jul 17, 2023 at 10:29:17AM +0200, Cezary Rojewski wrote: >> On 2023-07-12 5:48 PM, Andy Shevchenko wrote: >>> On Wed, Jul 12, 2023 at 11:10:48AM +0200, Cezary Rojewski wrote: ... >>>> +struct acpi_nhlt_endpoint * >>>> +acpi_nhlt_find_endpoint(const struct acpi_table_nhlt *tb, >>>> + int link_type, int dev_type, int dir, int bus_id) >>>> +{ >>>> + struct acpi_nhlt_endpoint *ep; >>> >>>> + if (!tb) >>>> + return ERR_PTR(-EINVAL); >>> >>> Just wondering how often we will have a caller that supplies NULL for tb. >> >> Depends on kernel's philosophy on public API. In general, public API should >> defend themselves from harmful and illegal callers. However, in low level >> areas 'illegal' is sometimes mistaken with illogical. In such cases double >> safety can be dropped. > > What do you put under "public"? ABI? Or just internally available API? > If the latter, we don't do defensive programming there, we usually just > add NULL(invalid data)-awareness to the free()-like functions. > >> Or, perhaps you were discussing return value here and ERR_PTR(-EINVAL) could >> be replaced by something else or even NULL. > > I prefer to get rid of those. Decided to do some manual tests on more exotic setups that are not part of our daily CI/CD routine and, completely getting rid of those ifs causes problems. Those setups are part of the market, expose DSP capabilities but have invalid BIOS configurations. Rather than just bringing back the if-statement, different solution came to my mind: static struct acpi_table_nhlt empty_nhlt = { .header = { .signature = ACPI_SIG_NHLT, }, }; struct acpi_table_nhlt *acpi_gbl_NHLT; EXPORT_SYMBOL_GPL(acpi_gbl_NHLT); acpi_status acpi_nhlt_get_gbl_table(void) { acpi_status status; status = acpi_get_table(ACPI_SIG_NHLT, 0, (struct acpi_table_header **)(&acpi_gbl_NHLT)); if (!acpi_gbl_NHLT) acpi_gbl_NHLT = &empty_nhlt; return status; } EXPORT_SYMBOL_GPL(acpi_nhlt_get_gbl_table); What do you think?
On Tue, Jul 18, 2023 at 01:11:03PM +0200, Cezary Rojewski wrote: > On 2023-07-17 11:47 AM, Andy Shevchenko wrote: > > On Mon, Jul 17, 2023 at 10:29:17AM +0200, Cezary Rojewski wrote: ... > > I prefer to get rid of those. > > Decided to do some manual tests on more exotic setups that are not part of > our daily CI/CD routine and, completely getting rid of those ifs causes > problems. Those setups are part of the market, expose DSP capabilities but > have invalid BIOS configurations. > > Rather than just bringing back the if-statement, different solution came to > my mind: > > static struct acpi_table_nhlt empty_nhlt = { > .header = { > .signature = ACPI_SIG_NHLT, > }, > }; > > struct acpi_table_nhlt *acpi_gbl_NHLT; > EXPORT_SYMBOL_GPL(acpi_gbl_NHLT); > > acpi_status acpi_nhlt_get_gbl_table(void) > { > acpi_status status; > > status = acpi_get_table(ACPI_SIG_NHLT, 0, (struct acpi_table_header > **)(&acpi_gbl_NHLT)); > if (!acpi_gbl_NHLT) > acpi_gbl_NHLT = &empty_nhlt; > return status; > } > EXPORT_SYMBOL_GPL(acpi_nhlt_get_gbl_table); > > What do you think? I think it's wonderful what you found and I dunno how I missed this. Go for this, definitely!
diff --git a/drivers/acpi/nhlt.c b/drivers/acpi/nhlt.c index 90d74d0d803e..c61cdfd78b74 100644 --- a/drivers/acpi/nhlt.c +++ b/drivers/acpi/nhlt.c @@ -6,8 +6,191 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#define pr_fmt(fmt) "ACPI: NHLT: " fmt + #include <linux/export.h> #include <acpi/nhlt.h> struct acpi_table_nhlt *acpi_gbl_NHLT; EXPORT_SYMBOL_GPL(acpi_gbl_NHLT); + +/** + * acpi_nhlt_endpoint_match - Verify if an endpoint matches criteria. + * @ep: the endpoint to check. + * @link_type: the hardware link type, e.g.: PDM or SSP. + * @dev_type: the device type. + * @dir: stream direction. + * @bus_id: the ID of virtual bus hosting the endpoint. + * + * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative + * value to ignore the parameter when matching. + * + * Return: %true if endpoint matches specified criteria or %false otherwise. + */ +bool acpi_nhlt_endpoint_match(const struct acpi_nhlt_endpoint *ep, + int link_type, int dev_type, int dir, int bus_id) +{ + return ep && + (link_type < 0 || ep->link_type == link_type) && + (dev_type < 0 || ep->device_type == dev_type) && + (dir < 0 || ep->direction == dir) && + (bus_id < 0 || ep->virtual_bus_id == bus_id); +} +EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_match); + +/** + * acpi_nhlt_find_endpoint - Search a NHLT table for an endpoint. + * @tb: the table to search. + * @link_type: the hardware link type, e.g.: PDM or SSP. + * @dev_type: the device type. + * @dir: stream direction. + * @bus_id: the ID of virtual bus hosting the endpoint. + * + * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative + * value to ignore the parameter during the search. + * + * Return: A pointer to endpoint matching the criteria, %NULL if not found or + * an ERR_PTR() otherwise. + */ +struct acpi_nhlt_endpoint * +acpi_nhlt_find_endpoint(const struct acpi_table_nhlt *tb, + int link_type, int dev_type, int dir, int bus_id) +{ + struct acpi_nhlt_endpoint *ep; + + if (!tb) + return ERR_PTR(-EINVAL); + + for_each_nhlt_endpoint(tb, ep) + if (acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id)) + return ep; + return NULL; +} +EXPORT_SYMBOL_GPL(acpi_nhlt_find_endpoint); + +/** + * acpi_nhlt_endpoint_find_fmtcfg - Search endpoint's formats configuration space + * for a specific format. + * @ep: the endpoint to search. + * @ch: number of channels. + * @rate: samples per second. + * @vbps: valid bits per sample. + * @bps: bits per sample. + * + * Return: A pointer to format matching the criteria, %NULL if not found or + * an ERR_PTR() otherwise. + */ +struct acpi_nhlt_format_config * +acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt_endpoint *ep, + u16 ch, u32 rate, u16 vbps, u16 bps) +{ + struct acpi_nhlt_wave_extensible *wav; + struct acpi_nhlt_format_config *fmt; + + if (!ep) + return ERR_PTR(-EINVAL); + + for_each_nhlt_endpoint_fmtcfg(ep, fmt) { + wav = &fmt->format; + + if (wav->channel_count == ch && + wav->valid_bits_per_sample == vbps && + wav->bits_per_sample == bps && + wav->samples_per_sec == rate) + return fmt; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_find_fmtcfg); + +/** + * acpi_nhlt_find_fmtcfg - Search a NHLT table for a specific format. + * @tb: the table to search. + * @link_type: the hardware link type, e.g.: PDM or SSP. + * @dev_type: the device type. + * @dir: stream direction. + * @bus_id: the ID of virtual bus hosting the endpoint. + * + * @ch: number of channels. + * @rate: samples per second. + * @vbps: valid bits per sample. + * @bps: bits per sample. + * + * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative + * value to ignore the parameter during the search. + * + * Return: A pointer to format matching the criteria, %NULL if not found or + * an ERR_PTR() otherwise. + */ +struct acpi_nhlt_format_config * +acpi_nhlt_find_fmtcfg(const struct acpi_table_nhlt *tb, + int link_type, int dev_type, int dir, int bus_id, + u16 ch, u32 rate, u16 vbps, u16 bps) +{ + struct acpi_nhlt_format_config *fmt; + struct acpi_nhlt_endpoint *ep; + + if (!tb) + return ERR_PTR(-EINVAL); + + for_each_nhlt_endpoint(tb, ep) { + if (!acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id)) + continue; + + fmt = acpi_nhlt_endpoint_find_fmtcfg(ep, ch, rate, vbps, bps); + if (fmt) + return fmt; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(acpi_nhlt_find_fmtcfg); + +/** + * acpi_nhlt_endpoint_dmic_count - Retrieve number of digital microphones for a PDM endpoint. + * @ep: the endpoint to return microphones count for. + * + * Return: A number of microphones or an error code if an invalid endpoint is provided. + */ +int acpi_nhlt_endpoint_dmic_count(const struct acpi_nhlt_endpoint *ep) +{ + struct acpi_nhlt_vendor_mic_devcfg *vendor_cfg; + struct acpi_nhlt_format_config *fmt; + struct acpi_nhlt_mic_devcfg *devcfg; + u16 max_ch = 0; + + if (!ep || ep->link_type != ACPI_NHLT_PDM) + return -EINVAL; + + /* Find max number of channels based on formats configuration. */ + for_each_nhlt_endpoint_fmtcfg(ep, fmt) + max_ch = max(fmt->format.channel_count, max_ch); + + /* If @ep not a mic array, fallback to channels count. */ + devcfg = acpi_nhlt_endpoint_mic_devcfg(ep); + if (!devcfg || devcfg->config_type != ACPI_NHLT_CONFIG_TYPE_MIC_ARRAY) + return max_ch; + + switch (devcfg->array_type) { + case ACPI_NHLT_SMALL_LINEAR_2ELEMENT: + case ACPI_NHLT_BIG_LINEAR_2ELEMENT: + return 2; + + case ACPI_NHLT_FIRST_GEOMETRY_LINEAR_4ELEMENT: + case ACPI_NHLT_PLANAR_LSHAPED_4ELEMENT: + case ACPI_NHLT_SECOND_GEOMETRY_LINEAR_4ELEMENT: + return 4; + + case ACPI_NHLT_VENDOR_DEFINED: + vendor_cfg = acpi_nhlt_endpoint_vendor_mic_devcfg(ep); + if (!vendor_cfg) + return -EINVAL; + return vendor_cfg->num_mics; + + default: + pr_warn("undefined mic array type: %#x\n", devcfg->array_type); + return max_ch; + } +} +EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_dmic_count); diff --git a/include/acpi/nhlt.h b/include/acpi/nhlt.h index 076aac41a74e..ba093fe871d5 100644 --- a/include/acpi/nhlt.h +++ b/include/acpi/nhlt.h @@ -149,4 +149,58 @@ acpi_nhlt_endpoint_fmtscfg(const struct acpi_nhlt_endpoint *ep) #define for_each_nhlt_endpoint_fmtcfg(ep, fmt) \ for_each_nhlt_fmtcfg(acpi_nhlt_endpoint_fmtscfg(ep), fmt) +#if IS_ENABLED(CONFIG_ACPI_NHLT) + +bool acpi_nhlt_endpoint_match(const struct acpi_nhlt_endpoint *ep, + int link_type, int dev_type, int dir, int bus_id); +struct acpi_nhlt_endpoint * +acpi_nhlt_find_endpoint(const struct acpi_table_nhlt *tb, + int link_type, int dev_type, int dir, int bus_id); +struct acpi_nhlt_format_config * +acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt_endpoint *ep, + u16 ch, u32 rate, u16 vbps, u16 bps); +struct acpi_nhlt_format_config * +acpi_nhlt_find_fmtcfg(const struct acpi_table_nhlt *tb, + int link_type, int dev_type, int dir, int bus_id, + u16 ch, u32 rate, u16 vpbs, u16 bps); +int acpi_nhlt_endpoint_dmic_count(const struct acpi_nhlt_endpoint *ep); + +#else /* !CONFIG_ACPI_NHLT */ + +static bool +acpi_nhlt_endpoint_match(const struct acpi_nhlt_endpoint *ep, + int link_type, int dev_type, int dir, int bus_id) +{ + return false; +} + +static inline struct acpi_nhlt_endpoint * +acpi_nhlt_find_endpoint(const struct acpi_table_nhlt *tb, + int link_type, int dev_type, int dir, int bus_id) +{ + return NULL; +} + +static inline struct acpi_nhlt_format_config * +acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt_endpoint *ep, + u16 ch, u32 rate, u16 vbps, u16 bps) +{ + return NULL; +} + +static inline struct acpi_nhlt_format_config * +acpi_nhlt_find_fmtcfg(const struct acpi_table_nhlt *tb, + int link_type, int dev_type, int dir, int bus_id, + u16 ch, u32 rate, u16 vpbs, u16 bps); +{ + return NULL; +} + +static inline int acpi_nhlt_endpoint_dmic_count(const struct acpi_nhlt_endpoint *ep) +{ + return 0; +} + +#endif /* !CONFIG_ACPI_NHLT */ + #endif /* __ACPI_NHLT_H__ */