1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2025, Qualcomm Technologies, Inc. and/or its subsidiaries.
#include <linux/export.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/printk.h>
#include <linux/component.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/regmap.h>
#include "wcd-common.h"
#define WCD_MIN_MICBIAS_MV 1000
#define WCD_DEF_MICBIAS_MV 1800
#define WCD_MAX_MICBIAS_MV 2850
#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m))
int wcd_get_micb_vout_ctl_val(struct device *dev, u32 micb_mv)
{
/* min micbias voltage is 1V and maximum is 2.85V */
if (micb_mv < WCD_MIN_MICBIAS_MV || micb_mv > WCD_MAX_MICBIAS_MV) {
dev_err(dev, "Unsupported micbias voltage (%u mV)\n", micb_mv);
return -EINVAL;
}
return (micb_mv - WCD_MIN_MICBIAS_MV) / 50;
}
EXPORT_SYMBOL_GPL(wcd_get_micb_vout_ctl_val);
static int wcd_get_micbias_val(struct device *dev, int micb_num, u32 *micb_mv)
{
char micbias[64];
int mv;
sprintf(micbias, "qcom,micbias%d-microvolt", micb_num);
if (of_property_read_u32(dev->of_node, micbias, &mv)) {
dev_err(dev, "%s value not found, using default\n", micbias);
mv = WCD_DEF_MICBIAS_MV;
} else {
/* convert it to milli volts */
mv = mv/1000;
}
if (micb_mv)
*micb_mv = mv;
mv = wcd_get_micb_vout_ctl_val(dev, mv);
if (mv < 0) {
dev_err(dev, "Unsupported %s voltage (%d mV), falling back to default (%d mV)\n",
micbias, mv, WCD_DEF_MICBIAS_MV);
return wcd_get_micb_vout_ctl_val(dev, WCD_DEF_MICBIAS_MV);
}
return mv;
}
int wcd_dt_parse_micbias_info(struct wcd_common *common)
{
int ret, i;
for (i = 0; i < common->max_bias; i++) {
ret = wcd_get_micbias_val(common->dev, i + 1, &common->micb_mv[i]);
if (ret < 0)
return ret;
common->micb_vout[i] = ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(wcd_dt_parse_micbias_info);
static int wcd_sdw_component_bind(struct device *dev, struct device *master, void *data)
{
pm_runtime_set_autosuspend_delay(dev, 3000);
pm_runtime_use_autosuspend(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
}
static void wcd_sdw_component_unbind(struct device *dev, struct device *master, void *data)
{
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_dont_use_autosuspend(dev);
}
const struct component_ops wcd_sdw_component_ops = {
.bind = wcd_sdw_component_bind,
.unbind = wcd_sdw_component_unbind,
};
EXPORT_SYMBOL_GPL(wcd_sdw_component_ops);
int wcd_update_status(struct sdw_slave *slave, enum sdw_slave_status status)
{
struct regmap *regmap = dev_get_regmap(&slave->dev, NULL);
if (regmap && status == SDW_SLAVE_ATTACHED) {
/* Write out any cached changes that happened between probe and attach */
regcache_cache_only(regmap, false);
return regcache_sync(regmap);
}
return 0;
}
EXPORT_SYMBOL_GPL(wcd_update_status);
int wcd_bus_config(struct sdw_slave *slave, struct sdw_bus_params *params)
{
sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), 0x01);
return 0;
}
EXPORT_SYMBOL_GPL(wcd_bus_config);
int wcd_interrupt_callback(struct sdw_slave *slave, struct irq_domain *slave_irq,
unsigned int wcd_intr_status0, unsigned int wcd_intr_status1,
unsigned int wcd_intr_status2)
{
struct regmap *regmap = dev_get_regmap(&slave->dev, NULL);
u32 sts1, sts2, sts3;
do {
handle_nested_irq(irq_find_mapping(slave_irq, 0));
regmap_read(regmap, wcd_intr_status0, &sts1);
regmap_read(regmap, wcd_intr_status1, &sts2);
regmap_read(regmap, wcd_intr_status2, &sts3);
} while (sts1 || sts2 || sts3);
return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(wcd_interrupt_callback);
MODULE_DESCRIPTION("Common Qualcomm WCD Codec helpers driver");
MODULE_LICENSE("GPL");
|