cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wcd938x-sdw.c (8544B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (c) 2021, Linaro Limited
      3
      4#include <linux/module.h>
      5#include <linux/slab.h>
      6#include <linux/platform_device.h>
      7#include <linux/device.h>
      8#include <linux/kernel.h>
      9#include <linux/component.h>
     10#include <linux/pm_runtime.h>
     11#include <linux/irq.h>
     12#include <linux/irqdomain.h>
     13#include <linux/of.h>
     14#include <linux/soundwire/sdw.h>
     15#include <linux/soundwire/sdw_type.h>
     16#include <linux/soundwire/sdw_registers.h>
     17#include <linux/regmap.h>
     18#include <sound/soc.h>
     19#include <sound/soc-dapm.h>
     20#include "wcd938x.h"
     21
     22#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m))
     23
     24static struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = {
     25	WCD_SDW_CH(WCD938X_HPH_L, WCD938X_HPH_PORT, BIT(0)),
     26	WCD_SDW_CH(WCD938X_HPH_R, WCD938X_HPH_PORT, BIT(1)),
     27	WCD_SDW_CH(WCD938X_CLSH, WCD938X_CLSH_PORT, BIT(0)),
     28	WCD_SDW_CH(WCD938X_COMP_L, WCD938X_COMP_PORT, BIT(0)),
     29	WCD_SDW_CH(WCD938X_COMP_R, WCD938X_COMP_PORT, BIT(1)),
     30	WCD_SDW_CH(WCD938X_LO, WCD938X_LO_PORT, BIT(0)),
     31	WCD_SDW_CH(WCD938X_DSD_L, WCD938X_DSD_PORT, BIT(0)),
     32	WCD_SDW_CH(WCD938X_DSD_R, WCD938X_DSD_PORT, BIT(1)),
     33};
     34
     35static struct wcd938x_sdw_ch_info wcd938x_sdw_tx_ch_info[] = {
     36	WCD_SDW_CH(WCD938X_ADC1, WCD938X_ADC_1_2_PORT, BIT(0)),
     37	WCD_SDW_CH(WCD938X_ADC2, WCD938X_ADC_1_2_PORT, BIT(1)),
     38	WCD_SDW_CH(WCD938X_ADC3, WCD938X_ADC_3_4_PORT, BIT(0)),
     39	WCD_SDW_CH(WCD938X_ADC4, WCD938X_ADC_3_4_PORT, BIT(1)),
     40	WCD_SDW_CH(WCD938X_DMIC0, WCD938X_DMIC_0_3_MBHC_PORT, BIT(0)),
     41	WCD_SDW_CH(WCD938X_DMIC1, WCD938X_DMIC_0_3_MBHC_PORT, BIT(1)),
     42	WCD_SDW_CH(WCD938X_MBHC, WCD938X_DMIC_0_3_MBHC_PORT, BIT(2)),
     43	WCD_SDW_CH(WCD938X_DMIC2, WCD938X_DMIC_0_3_MBHC_PORT, BIT(2)),
     44	WCD_SDW_CH(WCD938X_DMIC3, WCD938X_DMIC_0_3_MBHC_PORT, BIT(3)),
     45	WCD_SDW_CH(WCD938X_DMIC4, WCD938X_DMIC_4_7_PORT, BIT(0)),
     46	WCD_SDW_CH(WCD938X_DMIC5, WCD938X_DMIC_4_7_PORT, BIT(1)),
     47	WCD_SDW_CH(WCD938X_DMIC6, WCD938X_DMIC_4_7_PORT, BIT(2)),
     48	WCD_SDW_CH(WCD938X_DMIC7, WCD938X_DMIC_4_7_PORT, BIT(3)),
     49};
     50
     51static struct sdw_dpn_prop wcd938x_dpn_prop[WCD938X_MAX_SWR_PORTS] = {
     52	{
     53		.num = 1,
     54		.type = SDW_DPN_SIMPLE,
     55		.min_ch = 1,
     56		.max_ch = 8,
     57		.simple_ch_prep_sm = true,
     58	}, {
     59		.num = 2,
     60		.type = SDW_DPN_SIMPLE,
     61		.min_ch = 1,
     62		.max_ch = 4,
     63		.simple_ch_prep_sm = true,
     64	}, {
     65		.num = 3,
     66		.type = SDW_DPN_SIMPLE,
     67		.min_ch = 1,
     68		.max_ch = 4,
     69		.simple_ch_prep_sm = true,
     70	}, {
     71		.num = 4,
     72		.type = SDW_DPN_SIMPLE,
     73		.min_ch = 1,
     74		.max_ch = 4,
     75		.simple_ch_prep_sm = true,
     76	}, {
     77		.num = 5,
     78		.type = SDW_DPN_SIMPLE,
     79		.min_ch = 1,
     80		.max_ch = 4,
     81		.simple_ch_prep_sm = true,
     82	}
     83};
     84
     85struct device *wcd938x_sdw_device_get(struct device_node *np)
     86{
     87	return bus_find_device_by_of_node(&sdw_bus_type, np);
     88
     89}
     90EXPORT_SYMBOL_GPL(wcd938x_sdw_device_get);
     91
     92int wcd938x_swr_get_current_bank(struct sdw_slave *sdev)
     93{
     94	int bank;
     95
     96	bank  = sdw_read(sdev, SDW_SCP_CTRL);
     97
     98	return ((bank & 0x40) ? 1 : 0);
     99}
    100EXPORT_SYMBOL_GPL(wcd938x_swr_get_current_bank);
    101
    102int wcd938x_sdw_hw_params(struct wcd938x_sdw_priv *wcd,
    103			  struct snd_pcm_substream *substream,
    104			  struct snd_pcm_hw_params *params,
    105			  struct snd_soc_dai *dai)
    106{
    107	struct sdw_port_config port_config[WCD938X_MAX_SWR_PORTS];
    108	unsigned long ch_mask;
    109	int i, j;
    110
    111	wcd->sconfig.ch_count = 1;
    112	wcd->active_ports = 0;
    113	for (i = 0; i < WCD938X_MAX_SWR_PORTS; i++) {
    114		ch_mask = wcd->port_config[i].ch_mask;
    115
    116		if (!ch_mask)
    117			continue;
    118
    119		for_each_set_bit(j, &ch_mask, 4)
    120			wcd->sconfig.ch_count++;
    121
    122		port_config[wcd->active_ports] = wcd->port_config[i];
    123		wcd->active_ports++;
    124	}
    125
    126	wcd->sconfig.bps = 1;
    127	wcd->sconfig.frame_rate =  params_rate(params);
    128	if (wcd->is_tx)
    129		wcd->sconfig.direction = SDW_DATA_DIR_TX;
    130	else
    131		wcd->sconfig.direction = SDW_DATA_DIR_RX;
    132
    133	wcd->sconfig.type = SDW_STREAM_PCM;
    134
    135	return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig,
    136				    &port_config[0], wcd->active_ports,
    137				    wcd->sruntime);
    138}
    139EXPORT_SYMBOL_GPL(wcd938x_sdw_hw_params);
    140
    141int wcd938x_sdw_free(struct wcd938x_sdw_priv *wcd,
    142		     struct snd_pcm_substream *substream,
    143		     struct snd_soc_dai *dai)
    144{
    145	sdw_stream_remove_slave(wcd->sdev, wcd->sruntime);
    146
    147	return 0;
    148}
    149EXPORT_SYMBOL_GPL(wcd938x_sdw_free);
    150
    151int wcd938x_sdw_set_sdw_stream(struct wcd938x_sdw_priv *wcd,
    152			       struct snd_soc_dai *dai,
    153			       void *stream, int direction)
    154{
    155	wcd->sruntime = stream;
    156
    157	return 0;
    158}
    159EXPORT_SYMBOL_GPL(wcd938x_sdw_set_sdw_stream);
    160
    161static int wcd9380_update_status(struct sdw_slave *slave,
    162				 enum sdw_slave_status status)
    163{
    164	return 0;
    165}
    166
    167static int wcd9380_bus_config(struct sdw_slave *slave,
    168			      struct sdw_bus_params *params)
    169{
    170	sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank),  0x01);
    171
    172	return 0;
    173}
    174
    175static int wcd9380_interrupt_callback(struct sdw_slave *slave,
    176				      struct sdw_slave_intr_status *status)
    177{
    178	struct wcd938x_sdw_priv *wcd = dev_get_drvdata(&slave->dev);
    179	struct irq_domain *slave_irq = wcd->slave_irq;
    180	struct regmap *regmap = dev_get_regmap(&slave->dev, NULL);
    181	u32 sts1, sts2, sts3;
    182
    183	do {
    184		handle_nested_irq(irq_find_mapping(slave_irq, 0));
    185		regmap_read(regmap, WCD938X_DIGITAL_INTR_STATUS_0, &sts1);
    186		regmap_read(regmap, WCD938X_DIGITAL_INTR_STATUS_1, &sts2);
    187		regmap_read(regmap, WCD938X_DIGITAL_INTR_STATUS_2, &sts3);
    188
    189	} while (sts1 || sts2 || sts3);
    190
    191	return IRQ_HANDLED;
    192}
    193
    194static struct sdw_slave_ops wcd9380_slave_ops = {
    195	.update_status = wcd9380_update_status,
    196	.interrupt_callback = wcd9380_interrupt_callback,
    197	.bus_config = wcd9380_bus_config,
    198};
    199
    200static int wcd938x_sdw_component_bind(struct device *dev,
    201				      struct device *master, void *data)
    202{
    203	return 0;
    204}
    205
    206static void wcd938x_sdw_component_unbind(struct device *dev,
    207					 struct device *master, void *data)
    208{
    209}
    210
    211static const struct component_ops wcd938x_sdw_component_ops = {
    212	.bind   = wcd938x_sdw_component_bind,
    213	.unbind = wcd938x_sdw_component_unbind,
    214};
    215
    216static int wcd9380_probe(struct sdw_slave *pdev,
    217			 const struct sdw_device_id *id)
    218{
    219	struct device *dev = &pdev->dev;
    220	struct wcd938x_sdw_priv *wcd;
    221	int ret;
    222
    223	wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
    224	if (!wcd)
    225		return -ENOMEM;
    226
    227	/**
    228	 * Port map index starts with 0, however the data port for this codec
    229	 * are from index 1
    230	 */
    231	if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) {
    232		wcd->is_tx = true;
    233		ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
    234						 &pdev->m_port_map[1],
    235						 WCD938X_MAX_TX_SWR_PORTS);
    236	} else {
    237		ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping",
    238						 &pdev->m_port_map[1],
    239						 WCD938X_MAX_SWR_PORTS);
    240	}
    241
    242	if (ret < 0)
    243		dev_info(dev, "Static Port mapping not specified\n");
    244
    245	wcd->sdev = pdev;
    246	dev_set_drvdata(dev, wcd);
    247
    248	pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF |
    249					SDW_SCP_INT1_BUS_CLASH |
    250					SDW_SCP_INT1_PARITY;
    251	pdev->prop.lane_control_support = true;
    252	pdev->prop.simple_clk_stop_capable = true;
    253	if (wcd->is_tx) {
    254		pdev->prop.source_ports = GENMASK(WCD938X_MAX_SWR_PORTS, 0);
    255		pdev->prop.src_dpn_prop = wcd938x_dpn_prop;
    256		wcd->ch_info = &wcd938x_sdw_tx_ch_info[0];
    257		pdev->prop.wake_capable = true;
    258	} else {
    259		pdev->prop.sink_ports = GENMASK(WCD938X_MAX_SWR_PORTS, 0);
    260		pdev->prop.sink_dpn_prop = wcd938x_dpn_prop;
    261		wcd->ch_info = &wcd938x_sdw_rx_ch_info[0];
    262	}
    263
    264	pm_runtime_set_autosuspend_delay(dev, 3000);
    265	pm_runtime_use_autosuspend(dev);
    266	pm_runtime_mark_last_busy(dev);
    267	pm_runtime_set_active(dev);
    268	pm_runtime_enable(dev);
    269
    270	return component_add(dev, &wcd938x_sdw_component_ops);
    271}
    272
    273static const struct sdw_device_id wcd9380_slave_id[] = {
    274	SDW_SLAVE_ENTRY(0x0217, 0x10d, 0),
    275	{},
    276};
    277MODULE_DEVICE_TABLE(sdw, wcd9380_slave_id);
    278
    279static int __maybe_unused wcd938x_sdw_runtime_suspend(struct device *dev)
    280{
    281	struct regmap *regmap = dev_get_regmap(dev, NULL);
    282
    283	if (regmap) {
    284		regcache_cache_only(regmap, true);
    285		regcache_mark_dirty(regmap);
    286	}
    287	return 0;
    288}
    289
    290static int __maybe_unused wcd938x_sdw_runtime_resume(struct device *dev)
    291{
    292	struct regmap *regmap = dev_get_regmap(dev, NULL);
    293
    294	if (regmap) {
    295		regcache_cache_only(regmap, false);
    296		regcache_sync(regmap);
    297	}
    298
    299	pm_runtime_mark_last_busy(dev);
    300
    301	return 0;
    302}
    303
    304static const struct dev_pm_ops wcd938x_sdw_pm_ops = {
    305	SET_RUNTIME_PM_OPS(wcd938x_sdw_runtime_suspend, wcd938x_sdw_runtime_resume, NULL)
    306};
    307
    308
    309static struct sdw_driver wcd9380_codec_driver = {
    310	.probe	= wcd9380_probe,
    311	.ops = &wcd9380_slave_ops,
    312	.id_table = wcd9380_slave_id,
    313	.driver = {
    314		.name	= "wcd9380-codec",
    315		.pm = &wcd938x_sdw_pm_ops,
    316	}
    317};
    318module_sdw_driver(wcd9380_codec_driver);
    319
    320MODULE_DESCRIPTION("WCD938X SDW codec driver");
    321MODULE_LICENSE("GPL");