Files
kernel_xiaomi_sm8250/techpack/audio/dsp/avtimer.c
Michael Bestas 5850f8798e Merge tag 'LA.UM.9.15.r1-07300-KAMORTA.QSSI13.0' of https://git.codelinaro.org/clo/la/platform/vendor/opensource/audio-kernel into android13-4.19-kona
"LA.UM.9.15.r1-07300-KAMORTA.QSSI13.0"

* tag 'LA.UM.9.15.r1-07300-KAMORTA.QSSI13.0' of https://git.codelinaro.org/clo/la/platform/vendor/opensource/audio-kernel:
  ASoC: msm-pcm-q6-v2: Add dsp buf check
  ASoC: rouleur-mbhc: Enhance impedance calculation logic
  ASoC: rouleur-mbhc: Fix pop noise heard when headset insertion
  asoc: wsa881x: Fix to set the correct volume level
  soc: swr-mstr-ctrl: remove FIFO_Flush for overflow
  asoc: Add routing control for qrb5165 BT HFP usecase
  soc: swr-mstr: Store and compare dev_addr along with dev_num
  Asoc: fix integer overflow for long duration offload playback
  asoc: Correct the range of dev_token for usb headset.
  asoc: pcm-noirq: set substream state to disconnect when ssr happens
  soc: pinctrl-lpi-legacy : Add nullptr check
  asoc: msm-pcm-routing: Assign default topology when app_type is zero
  asoc: notify dp driver after disp codec probe
  asoc: codecs: fix race condition of core vote and reg access
  ASoC: wcd937x: Add check for ULP irrespective of the Class
  asoc: codecs: Add nullptr check
  ASoC: wcd937x: Update QCRG sequence for wcd937x
  asoc: changes to fix KW errors.
  asoc: changes to fix KW errors.
  ASoC: pcm-routing: Add WSA VI sense macro to support capture
  ASoC: wcd937x: Update EAR PA CnP FSM sequence
  soc: swr-mstr: update component and interrupt enable sequence
  ASoC: wcd937x: Update EAR PA CnP FSM sequence
  soc: add check condition before enabling irq
  ASoC: wcd937x: Add change for LDOL VOUT accuracy calibration
  ASoC: dsp: Synchronise adm commands
  ASoC: wcd937x: Add change to micbias reg for new tanggu fab id
  ASoC: wcd937x: Update register value for new fab id for tanggu
  dsp:q6afe: Resolve crash in q6afe_get_avcs_modules
  ASoC: wcd937x: Change resistor divider output for hph if fab id is enabled
  dsp: adm: add to check output/input channel value
  asoc: Parse wcd-datalane-mismatch property to update rx frame config for khaje
  asoc: wcd-cpe: Set the dma_mask for cpe_device
  asoc: Parse wcd-datalane-mismatch property to update rx frame config for khaje
  ASoC: msm8952: Defer until pcm voice probe done
  q6afe: Fix out of bound access of clkinfo_per_port
  asoc: Register CDC_DMA interface only if enabled
  config: sdm450: Disable CDC_DMA interfaces
  asoc: codecs: add null check before access
  asoc: fix KW issue of using uninitialized variable
  audio-kernel: enable wsa883x compilation on qrb5165
  ASoC: Enable wsa881x-temp-sensor for bengal
  ASoC: msm8952: avoid static route between cpu and codec dai
  asoc: codecs: Fix LPASS register access during bootup
  ASoC: wcd: Add check to stop invalid memory access
  afe: release codec port when afe starts failed
  ASoC: Changes to enable codec clk for wcd9335
  Asoc: check for invalid voice session id
  SoC: codecs: Fix slim_tx port configuration for wcd9335
  ASoC: Get the component handle for cpe and tasha
  ASoC: wcd937x: Change vbg fine voltage to 0.5V for wcd937x second source
  audio-kernel: enable wsa883x compilation on sxr2130
  dsp: fix out of bound access for cal_data size
  dsp: update size check for set/get VI param function
  config: sdm450: Disable TDM and AUXPCM interfaces
  asoc: msm_sdw: Revert adsp ready check for read/write
  ASoC: SDM660: Avoid static route between cpu and codec dai
  asoc: bolero: Update PCM_RATE based delay for amic
  ASoC: SDM660: Avoid static route between cpu and codec dai
  ASoC: Fix for SSR issue on msm8953
  ASoC: error check for backend index and MCLK src
  Asoc: sdm450: Add capture support for Multimedia10 FE
  asoc: add proxy ports for call screening in machine driver
  asoc: msm: Fix compilation error when enable TDM and disable MI2S
  Makefile: Correct TARGET_SUPPORT entry for qrb5165
  dsp: update size check for get VI param function
  asoc: codecs: add child devices after completing initialization
  asoc: bolero: Add core_vote before gfmux access
  dsp: Disable q6_pdr build for msm8937 and msm8953
  asoc: Fixed wsa881x codec machine driver callback
  asoc: codec: enable the cpe on tasha
  Use proper TARGET_SUPPORT entry for sxr2130
  asoc: wsa881x: Fix to set the correct volume level
  ASoC: ep92: Add external mclk support
  ASoC: msm: Add WCD interrupt config for SDA660
  dsp: Unload the BT modules when APR handle is NULL
  asoc: Update copy_to_user to requested buffer size
  ASoC: Audio bringup changes for msm8937_32go.
  asoc: Add check to handle negative value passed for num_app_cfg_type
  ASoC: QCS405: Additional TDM samplerates and channels
  asoc: qcs405: Add support for TDM interface
  asoc: Add support for QUAT and SEN TDM interface
  asoc: qcs405: Update clk src string from dts
  dsp: Add support to update and store clk src values
  dsp: afe topology deregister
  ASoC: qcs405: Add external mclk support
  ASoC: Add external mclk mixer support
  dsp: Fix improper mutex unlock in afe close
  ASoC: wsa883x: Handle PA_ERR interrupt on WSA speaker
  ASoC: Add support to send voice UI port id to afe
  Revert "dsp: avtimer: Add adsp ready check before accessing avtimer registers"
  asoc: wsa881x: Fix crash while capturing swr_slv codec dump
  dsp: add change to enable preemption at cal_utils_dealloc_cal
  asoc: msm_sdw: Handle locking for mclk enable flag
  asoc: codecs: sdm660_cdc: Fix LPASS register access during PDR
  ASoC: lagoon: add support for CPS speaker protection
  dsp: add change to enable preemption at cal_utils_dealloc_cal
  ASoC: tx-macro: Allow regcache sync during clock enablement
  ASoC: wsa883x: add function to get swr device number
  dsp: add change to handle use-after-free in cal_utils_is_cal_stale
  soc: swr-mstr: Check if fifo available before bulk write
  asoc: bolero: control wakeup of swr_tx during clock setup
  soc: swr-mstr: Resolve swr overflow, underflow errors for wsa
  soc: swr-mstr: Update soundwire state to SSR during UP
  dsp: add change to fix use-after-free issue
  asoc: msm-pcm-q6-v2: Update memset for period size
  asoc: va-macro: Allow swr switch at powerup/shutdown
  ASoC: SDM660: Avoid static route between cpu and codec dai
  soc: Synchronize RESET event and lpi ops in legacy driver
  ASoC: codec: Enable wcd-spi driver compilation
  asoc: wcd-spi: Set the dma_mask for spi_device
  ASoC: audio-ext-clk: Add pmi clk support for tasha
  asoc: add null check for pcm pointer of snd_pcm_volume
  dsp: add afe function to send cps configuration
  asoc: Add 32bit support in meta i2s
  asoc: va-macro: Update widget order for VA_MCLK and VA_SWR_PWR
  dsp: Add param header size based on the instance id support
  dsp: q6core: Check q6core avs state to be modules ready
  ASoC: dsp: Synchronise adm and rtac commands
  asoc: codecs: Add support to dump rouleur swr slave regs
  asoc: sdm660: Correct the dev_id for hdmi backend.
  Revert "ASoC: codecs: handle device disconnect during SSR/PDR"
  ASoC: wsa883x: Update register sequence to reduce CnP
  asoc: sdm660: add proxy ports for call screening in machine driver
  soc: swr-mstr: Fix headset detect issue due to master.num_dev not updated
  soc: swr-mstr: Fix random headset detection issue on scuba
  dsp: Feedback path cfg to support 3rd party spkr protection algorithms.
  ASOC: SPV4 set correct number of speakers.
  ASoC: wsa883x: enable vbat adc filter
  config: kona: Disable MI2S, TDM and AUXPCM interfaces
  dsp: fix compilation issue in q6lsm_callback function
  asoc: Register widgets and intercons only when supported
  soc: swr-mstr: Fix random headset detection issue on scuba
  ASoC: codecs: handle device disconnect during SSR
  asoc: dsp: Fix possible invalid memory access due to race condition
  ASoC: rouleur-mbhc: Fix impedance detection issue on rouleur
  dsp: Add support for GET_PARAMS

 Conflicts:
	techpack/audio/asoc/kona.c
	techpack/audio/asoc/msm-pcm-routing-v2.c
	techpack/audio/dsp/q6core.c
	techpack/audio/dsp/q6lsm.c
	techpack/audio/include/dsp/audio_cal_utils.h
	techpack/audio/soc/swr-mstr-ctrl.c
	techpack/audio/soc/swr-mstr-ctrl.h

Change-Id: Id77d1c17f36618427f929d7830200733a0e8b460
2023-04-01 01:07:23 +03:00

636 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2015, 2017-2019 The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/avtimer.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/wait.h>
#include <linux/sched.h>
#if IS_ENABLED(CONFIG_AVTIMER_LEGACY)
#include <media/msmb_isp.h>
#endif
#include <ipc/apr.h>
#include <dsp/q6core.h>
#define DEVICE_NAME "avtimer"
#define TIMEOUT_MS 1000
#define CORE_CLIENT 1
#define TEMP_PORT ((CORE_CLIENT << 8) | 0x0001)
#define SSR_WAKETIME 1000
#define Q6_READY_RETRY 250
#define Q6_READY_MAX_RETRIES 40
#define AVCS_CMD_REMOTE_AVTIMER_VOTE_REQUEST 0x00012914
#define AVCS_CMD_RSP_REMOTE_AVTIMER_VOTE_REQUEST 0x00012915
#define AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST 0x00012916
#define AVTIMER_REG_CNT 2
struct adsp_avt_timer {
struct apr_hdr hdr;
union {
char client_name[8];
u32 avtimer_handle;
};
} __packed;
static int major;
struct avtimer_t {
struct apr_svc *core_handle_q;
struct cdev myc;
struct class *avtimer_class;
struct mutex avtimer_lock;
int avtimer_open_cnt;
struct delayed_work ssr_dwork;
wait_queue_head_t adsp_resp_wait;
int enable_timer_resp_received;
int timer_handle;
void __iomem *p_avtimer_msw;
void __iomem *p_avtimer_lsw;
uint32_t clk_div;
uint32_t clk_mult;
atomic_t adsp_ready;
int num_retries;
};
static struct avtimer_t avtimer;
static void avcs_set_isp_fptr(bool enable);
static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv)
{
uint32_t *payload1;
if (!data) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
pr_debug("%s: core msg: payload len = %u, apr resp opcode = 0x%X\n",
__func__, data->payload_size, data->opcode);
switch (data->opcode) {
case APR_BASIC_RSP_RESULT:{
if (!data->payload_size) {
pr_err("%s: APR_BASIC_RSP_RESULT No Payload ",
__func__);
return 0;
}
payload1 = data->payload;
if (data->payload_size < 2 * sizeof(uint32_t)) {
pr_err("%s: payload has invalid size %d\n",
__func__, data->payload_size);
return -EINVAL;
}
switch (payload1[0]) {
case AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST:
pr_debug("%s: Cmd = TIMER RELEASE status[0x%x]\n",
__func__, payload1[1]);
break;
default:
pr_err("Invalid cmd rsp[0x%x][0x%x]\n",
payload1[0], payload1[1]);
break;
}
break;
}
case RESET_EVENTS:{
pr_debug("%s: Reset event received in AV timer\n", __func__);
apr_reset(avtimer.core_handle_q);
avtimer.core_handle_q = NULL;
avtimer.avtimer_open_cnt = 0;
atomic_set(&avtimer.adsp_ready, 0);
schedule_delayed_work(&avtimer.ssr_dwork,
msecs_to_jiffies(SSR_WAKETIME));
break;
}
case AVCS_CMD_RSP_REMOTE_AVTIMER_VOTE_REQUEST:
if (data->payload_size < sizeof(uint32_t)) {
pr_err("%s: payload has invalid size %d\n",
__func__, data->payload_size);
return -EINVAL;
}
payload1 = data->payload;
pr_debug("%s: RSP_REMOTE_AVTIMER_VOTE_REQUEST handle %x\n",
__func__, payload1[0]);
avtimer.timer_handle = payload1[0];
avtimer.enable_timer_resp_received = 1;
wake_up(&avtimer.adsp_resp_wait);
break;
default:
pr_err("%s: Message adspcore svc: %d\n",
__func__, data->opcode);
break;
}
return 0;
}
int avcs_core_open(void)
{
if (!avtimer.core_handle_q)
avtimer.core_handle_q = apr_register("ADSP", "CORE",
aprv2_core_fn_q, TEMP_PORT, NULL);
pr_debug("%s: Open_q %p\n", __func__, avtimer.core_handle_q);
if (!avtimer.core_handle_q) {
pr_err("%s: Unable to register CORE\n", __func__);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(avcs_core_open);
static int avcs_core_disable_avtimer(int timerhandle)
{
int rc = -EINVAL;
struct adsp_avt_timer payload;
if (!timerhandle) {
pr_err("%s: Invalid timer handle\n", __func__);
return -EINVAL;
}
memset(&payload, 0, sizeof(payload));
rc = avcs_core_open();
if (!rc && avtimer.core_handle_q) {
payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
payload.hdr.pkt_size =
sizeof(struct adsp_avt_timer);
payload.hdr.src_svc = avtimer.core_handle_q->id;
payload.hdr.src_domain = APR_DOMAIN_APPS;
payload.hdr.dest_domain = APR_DOMAIN_ADSP;
payload.hdr.dest_svc = APR_SVC_ADSP_CORE;
payload.hdr.src_port = TEMP_PORT;
payload.hdr.dest_port = TEMP_PORT;
payload.hdr.token = CORE_CLIENT;
payload.hdr.opcode = AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST;
payload.avtimer_handle = timerhandle;
pr_debug("%s: disable avtimer opcode %x handle %x\n",
__func__, payload.hdr.opcode, payload.avtimer_handle);
rc = apr_send_pkt(avtimer.core_handle_q,
(uint32_t *)&payload);
if (rc < 0)
pr_err("%s: Enable AVtimer failed op[0x%x]rc[%d]\n",
__func__, payload.hdr.opcode, rc);
else
rc = 0;
}
return rc;
}
static int avcs_core_enable_avtimer(char *client_name)
{
int rc = -EINVAL, ret = -EINVAL;
struct adsp_avt_timer payload;
if (!client_name) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
memset(&payload, 0, sizeof(payload));
rc = avcs_core_open();
if (!rc && avtimer.core_handle_q) {
avtimer.enable_timer_resp_received = 0;
payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
payload.hdr.pkt_size =
sizeof(struct adsp_avt_timer);
payload.hdr.src_svc = avtimer.core_handle_q->id;
payload.hdr.src_domain = APR_DOMAIN_APPS;
payload.hdr.dest_domain = APR_DOMAIN_ADSP;
payload.hdr.dest_svc = APR_SVC_ADSP_CORE;
payload.hdr.src_port = TEMP_PORT;
payload.hdr.dest_port = TEMP_PORT;
payload.hdr.token = CORE_CLIENT;
payload.hdr.opcode = AVCS_CMD_REMOTE_AVTIMER_VOTE_REQUEST;
strlcpy(payload.client_name, client_name,
sizeof(payload.client_name));
pr_debug("%s: enable avtimer opcode %x client name %s\n",
__func__, payload.hdr.opcode, payload.client_name);
rc = apr_send_pkt(avtimer.core_handle_q,
(uint32_t *)&payload);
if (rc < 0) {
pr_err("%s: Enable AVtimer failed op[0x%x]rc[%d]\n",
__func__, payload.hdr.opcode, rc);
goto bail;
} else
rc = 0;
ret = wait_event_timeout(avtimer.adsp_resp_wait,
(avtimer.enable_timer_resp_received == 1),
msecs_to_jiffies(TIMEOUT_MS));
if (!ret) {
pr_err("%s: wait_event timeout for Enable timer\n",
__func__);
rc = -ETIMEDOUT;
}
if (rc)
avtimer.timer_handle = 0;
}
bail:
return rc;
}
int avcs_core_disable_power_collapse(int enable)
{
int rc = 0;
mutex_lock(&avtimer.avtimer_lock);
if (enable) {
if (avtimer.avtimer_open_cnt) {
avtimer.avtimer_open_cnt++;
pr_debug("%s: opened avtimer open count=%d\n",
__func__, avtimer.avtimer_open_cnt);
rc = 0;
goto done;
}
rc = avcs_core_enable_avtimer("timer");
if (!rc) {
avtimer.avtimer_open_cnt++;
atomic_set(&avtimer.adsp_ready, 1);
}
} else {
if (avtimer.avtimer_open_cnt > 0) {
avtimer.avtimer_open_cnt--;
if (!avtimer.avtimer_open_cnt) {
rc = avcs_core_disable_avtimer(
avtimer.timer_handle);
avtimer.timer_handle = 0;
atomic_set(&avtimer.adsp_ready, 0);
}
}
}
done:
mutex_unlock(&avtimer.avtimer_lock);
return rc;
}
EXPORT_SYMBOL(avcs_core_disable_power_collapse);
static void reset_work(struct work_struct *work)
{
if (q6core_is_adsp_ready()) {
avcs_core_disable_power_collapse(1);
avtimer.num_retries = Q6_READY_MAX_RETRIES;
return;
}
pr_debug("%s:Q6 not ready-retry after sometime\n", __func__);
if (--avtimer.num_retries > 0) {
schedule_delayed_work(&avtimer.ssr_dwork,
msecs_to_jiffies(Q6_READY_RETRY));
} else {
pr_err("%s: Q6 failed responding after multiple retries\n",
__func__);
avtimer.num_retries = Q6_READY_MAX_RETRIES;
}
}
int avcs_core_query_timer(uint64_t *avtimer_tick)
{
uint32_t avtimer_msw = 0, avtimer_lsw = 0;
uint64_t avtimer_tick_temp;
if (!atomic_read(&avtimer.adsp_ready)) {
pr_debug("%s:In SSR, return\n", __func__);
return -ENETRESET;
}
avtimer_lsw = ioread32(avtimer.p_avtimer_lsw);
avtimer_msw = ioread32(avtimer.p_avtimer_msw);
avtimer_tick_temp = (uint64_t)((uint64_t)avtimer_msw << 32)
| avtimer_lsw;
*avtimer_tick = mul_u64_u32_div(avtimer_tick_temp, avtimer.clk_mult,
avtimer.clk_div);
pr_debug_ratelimited("%s:Avtimer: msw: %u, lsw: %u, tick: %llu\n",
__func__,
avtimer_msw, avtimer_lsw, *avtimer_tick);
return 0;
}
EXPORT_SYMBOL(avcs_core_query_timer);
/*
* avcs_core_query_timer_offset:
* derive offset between system clock & avtimer clock
*
* @ avoffset: offset between system clock & avtimer clock
* @ clock_id: clock id to get the system time
*
*/
int avcs_core_query_timer_offset(int64_t *av_offset, int32_t clock_id)
{
uint32_t avtimer_msw = 0, avtimer_lsw = 0;
uint64_t avtimer_tick_temp, avtimer_tick, sys_time = 0;
struct timespec ts;
if ((avtimer.p_avtimer_lsw == NULL) ||
(avtimer.p_avtimer_msw == NULL)) {
return -EINVAL;
}
memset(&ts, 0, sizeof(struct timespec));
avtimer_lsw = ioread32(avtimer.p_avtimer_lsw);
avtimer_msw = ioread32(avtimer.p_avtimer_msw);
switch (clock_id) {
case CLOCK_MONOTONIC_RAW:
getrawmonotonic(&ts);
break;
case CLOCK_BOOTTIME:
get_monotonic_boottime(&ts);
break;
case CLOCK_MONOTONIC:
ktime_get_ts(&ts);
break;
case CLOCK_REALTIME:
ktime_get_real_ts(&ts);
break;
default:
pr_debug("%s: unsupported clock id %d\n", __func__, clock_id);
return -EINVAL;
}
sys_time = ts.tv_sec * 1000000LL + div64_u64(ts.tv_nsec, 1000);
avtimer_tick_temp = (uint64_t)((uint64_t)avtimer_msw << 32) |
avtimer_lsw;
avtimer_tick = mul_u64_u32_div(avtimer_tick_temp, avtimer.clk_mult,
avtimer.clk_div);
*av_offset = sys_time - avtimer_tick;
pr_debug("%s: sys_time: %llu, offset %lld, avtimer tick %lld\n",
__func__, sys_time, *av_offset, avtimer_tick);
return 0;
}
EXPORT_SYMBOL(avcs_core_query_timer_offset);
#if IS_ENABLED(CONFIG_AVTIMER_LEGACY)
static void avcs_set_isp_fptr(bool enable)
{
struct avtimer_fptr_t av_fptr;
if (enable) {
av_fptr.fptr_avtimer_open = avcs_core_open;
av_fptr.fptr_avtimer_enable = avcs_core_disable_power_collapse;
av_fptr.fptr_avtimer_get_time = avcs_core_query_timer;
msm_isp_set_avtimer_fptr(av_fptr);
} else {
av_fptr.fptr_avtimer_open = NULL;
av_fptr.fptr_avtimer_enable = NULL;
av_fptr.fptr_avtimer_get_time = NULL;
msm_isp_set_avtimer_fptr(av_fptr);
}
}
#else
static void avcs_set_isp_fptr(bool enable)
{
}
#endif
static int avtimer_open(struct inode *inode, struct file *file)
{
return avcs_core_disable_power_collapse(1);
}
static int avtimer_release(struct inode *inode, struct file *file)
{
return avcs_core_disable_power_collapse(0);
}
/*
* ioctl call provides GET_AVTIMER
*/
static long avtimer_ioctl(struct file *file, unsigned int ioctl_num,
unsigned long ioctl_param)
{
switch (ioctl_num) {
case IOCTL_GET_AVTIMER_TICK:
{
uint64_t avtimer_tick = 0;
int rc;
rc = avcs_core_query_timer(&avtimer_tick);
if (rc) {
pr_err("%s: Error: Invalid AV Timer tick, rc = %d\n",
__func__, rc);
return rc;
}
pr_debug_ratelimited("%s: AV Timer tick: time %llx\n",
__func__, avtimer_tick);
if (copy_to_user((void __user *)ioctl_param, &avtimer_tick,
sizeof(avtimer_tick))) {
pr_err("%s: copy_to_user failed\n", __func__);
return -EFAULT;
}
}
break;
default:
pr_err("%s: invalid cmd\n", __func__);
return -EINVAL;
}
return 0;
}
static const struct file_operations avtimer_fops = {
.unlocked_ioctl = avtimer_ioctl,
.compat_ioctl = avtimer_ioctl,
.open = avtimer_open,
.release = avtimer_release
};
static int dev_avtimer_probe(struct platform_device *pdev)
{
int result = 0;
dev_t dev = MKDEV(major, 0);
struct device *device_handle;
struct resource *reg_lsb = NULL, *reg_msb = NULL;
uint32_t clk_div_val;
uint32_t clk_mult_val;
if (!pdev) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
reg_lsb = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "avtimer_lsb_addr");
if (!reg_lsb) {
dev_err(&pdev->dev, "%s: Looking up %s property",
"avtimer_lsb_addr", __func__);
return -EINVAL;
}
reg_msb = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "avtimer_msb_addr");
if (!reg_msb) {
dev_err(&pdev->dev, "%s: Looking up %s property",
"avtimer_msb_addr", __func__);
return -EINVAL;
}
INIT_DELAYED_WORK(&avtimer.ssr_dwork, reset_work);
avtimer.p_avtimer_lsw = devm_ioremap_nocache(&pdev->dev,
reg_lsb->start, resource_size(reg_lsb));
if (!avtimer.p_avtimer_lsw) {
dev_err(&pdev->dev, "%s: ioremap failed for lsb avtimer register",
__func__);
return -ENOMEM;
}
avtimer.p_avtimer_msw = devm_ioremap_nocache(&pdev->dev,
reg_msb->start, resource_size(reg_msb));
if (!avtimer.p_avtimer_msw) {
dev_err(&pdev->dev, "%s: ioremap failed for msb avtimer register",
__func__);
goto unmap;
}
avtimer.num_retries = Q6_READY_MAX_RETRIES;
/* get the device number */
if (major)
result = register_chrdev_region(dev, 1, DEVICE_NAME);
else {
result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
major = MAJOR(dev);
}
if (result < 0) {
dev_err(&pdev->dev, "%s: Registering avtimer device failed\n",
__func__);
goto unmap;
}
avtimer.avtimer_class = class_create(THIS_MODULE, "avtimer");
if (IS_ERR(avtimer.avtimer_class)) {
result = PTR_ERR(avtimer.avtimer_class);
dev_err(&pdev->dev, "%s: Error creating avtimer class: %d\n",
__func__, result);
goto unregister_chrdev_region;
}
cdev_init(&avtimer.myc, &avtimer_fops);
result = cdev_add(&avtimer.myc, dev, 1);
if (result < 0) {
dev_err(&pdev->dev, "%s: Registering file operations failed\n",
__func__);
goto class_destroy;
}
device_handle = device_create(avtimer.avtimer_class,
NULL, avtimer.myc.dev, NULL, "avtimer");
if (IS_ERR(device_handle)) {
result = PTR_ERR(device_handle);
pr_err("%s: device_create failed: %d\n", __func__, result);
goto class_destroy;
}
init_waitqueue_head(&avtimer.adsp_resp_wait);
mutex_init(&avtimer.avtimer_lock);
avtimer.avtimer_open_cnt = 0;
pr_debug("%s: Device create done for avtimer major=%d\n",
__func__, major);
if (of_property_read_u32(pdev->dev.of_node,
"qcom,clk-div", &clk_div_val))
avtimer.clk_div = 1;
else
avtimer.clk_div = clk_div_val;
if (of_property_read_u32(pdev->dev.of_node,
"qcom,clk-mult", &clk_mult_val))
avtimer.clk_mult = 1;
else
avtimer.clk_mult = clk_mult_val;
avcs_set_isp_fptr(true);
pr_debug("%s: avtimer.clk_div = %d, avtimer.clk_mult = %d\n",
__func__, avtimer.clk_div, avtimer.clk_mult);
return 0;
class_destroy:
class_destroy(avtimer.avtimer_class);
unregister_chrdev_region:
unregister_chrdev_region(MKDEV(major, 0), 1);
unmap:
if (avtimer.p_avtimer_lsw)
devm_iounmap(&pdev->dev, avtimer.p_avtimer_lsw);
if (avtimer.p_avtimer_msw)
devm_iounmap(&pdev->dev, avtimer.p_avtimer_msw);
avtimer.p_avtimer_lsw = NULL;
avtimer.p_avtimer_msw = NULL;
return result;
}
static int dev_avtimer_remove(struct platform_device *pdev)
{
pr_debug("%s: dev_avtimer_remove\n", __func__);
if (avtimer.p_avtimer_lsw)
devm_iounmap(&pdev->dev, avtimer.p_avtimer_lsw);
if (avtimer.p_avtimer_msw)
devm_iounmap(&pdev->dev, avtimer.p_avtimer_msw);
device_destroy(avtimer.avtimer_class, avtimer.myc.dev);
cdev_del(&avtimer.myc);
class_destroy(avtimer.avtimer_class);
unregister_chrdev_region(MKDEV(major, 0), 1);
avcs_set_isp_fptr(false);
return 0;
}
static const struct of_device_id avtimer_machine_of_match[] = {
{ .compatible = "qcom,avtimer", },
{},
};
static struct platform_driver dev_avtimer_driver = {
.probe = dev_avtimer_probe,
.remove = dev_avtimer_remove,
.driver = {
.name = "dev_avtimer",
.of_match_table = avtimer_machine_of_match,
.suppress_bind_attrs = true,
},
};
int __init avtimer_init(void)
{
s32 rc;
rc = platform_driver_register(&dev_avtimer_driver);
if (rc < 0) {
pr_err("%s: platform_driver_register failed\n", __func__);
goto error_platform_driver;
}
pr_debug("%s: dev_avtimer_init : done\n", __func__);
return 0;
error_platform_driver:
pr_err("%s: encounterd error\n", __func__);
return rc;
}
void avtimer_exit(void)
{
platform_driver_unregister(&dev_avtimer_driver);
}
MODULE_DESCRIPTION("avtimer driver");
MODULE_LICENSE("GPL v2");