Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/sound/soc/codecs/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 15 kB image not shown  

Quelle  rt712-sdca-sdw.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
//
// rt712-sdca-sdw.c -- rt712 SDCA ALSA SoC audio driver
//
// Copyright(c) 2023 Realtek Semiconductor Corp.
//
//

#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h>
#include "rt712-sdca.h"
#include "rt712-sdca-sdw.h"

static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg)
{
 switch (reg) {
 case 0x201a ... 0x201f:
 case 0x2029 ... 0x202a:
 case 0x202d ... 0x2034:
 case 0x2230 ... 0x2232:
 case 0x2f01 ... 0x2f0a:
 case 0x2f35 ... 0x2f36:
 case 0x2f50:
 case 0x2f54:
 case 0x2f58 ... 0x2f5d:
 case 0x3201:
 case 0x320c:
 case 0x3301 ... 0x3303:
 case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0):
 case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0):
 case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
  SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
 case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
 case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
 case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
 case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
 case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2:
  return true;
 default:
  return false;
 }
}

static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg)
{
 switch (reg) {
 case 0x201b:
 case 0x201c:
 case 0x201d:
 case 0x201f:
 case 0x202d ... 0x202f:
 case 0x2230:
 case 0x2f01:
 case 0x2f35:
 case 0x320c:
 case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0):
 case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
  SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
 case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
 case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
 case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
 case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
 case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2:
  return true;
 default:
  return false;
 }
}

static bool rt712_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
{
 switch (reg) {
 case 0x2000000 ... 0x200008e:
 case 0x5300000 ... 0x530000e:
 case 0x5400000 ... 0x540000e:
 case 0x5600000 ... 0x5600008:
 case 0x5700000 ... 0x570000d:
 case 0x5800000 ... 0x5800021:
 case 0x5900000 ... 0x5900028:
 case 0x5a00000 ... 0x5a00009:
 case 0x5b00000 ... 0x5b00051:
 case 0x5c00000 ... 0x5c0009a:
 case 0x5d00000 ... 0x5d00009:
 case 0x5f00000 ... 0x5f00030:
 case 0x6100000 ... 0x61000f1:
 case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01):
 case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02):
 case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01):
 case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02):
 case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01):
 case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02):
 case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01):
 case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02):
 case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03):
 case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04):
 case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01):
 case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02):
 case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03):
 case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04):
  return true;
 default:
  return false;
 }
}

static bool rt712_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
{
 switch (reg) {
 case 0x2000000:
 case 0x200001a:
 case 0x2000020:
 case 0x2000024:
 case 0x2000030:
 case 0x2000046:
 case 0x200008a:
 case 0x5800000:
 case 0x5800001:
 case 0x6100008:
  return true;
 default:
  return false;
 }
}

static const struct regmap_config rt712_sdca_regmap = {
 .reg_bits = 32,
 .val_bits = 8,
 .readable_reg = rt712_sdca_readable_register,
 .volatile_reg = rt712_sdca_volatile_register,
 .max_register = 0x44ffffff,
 .reg_defaults = rt712_sdca_reg_defaults,
 .num_reg_defaults = ARRAY_SIZE(rt712_sdca_reg_defaults),
 .cache_type = REGCACHE_MAPLE,
 .use_single_read = true,
 .use_single_write = true,
};

static const struct regmap_config rt712_sdca_mbq_regmap = {
 .name = "sdw-mbq",
 .reg_bits = 32,
 .val_bits = 16,
 .readable_reg = rt712_sdca_mbq_readable_register,
 .volatile_reg = rt712_sdca_mbq_volatile_register,
 .max_register = 0x41000312,
 .reg_defaults = rt712_sdca_mbq_defaults,
 .num_reg_defaults = ARRAY_SIZE(rt712_sdca_mbq_defaults),
 .cache_type = REGCACHE_MAPLE,
 .use_single_read = true,
 .use_single_write = true,
};

static int rt712_sdca_update_status(struct sdw_slave *slave,
    enum sdw_slave_status status)
{
 struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);

 if (status == SDW_SLAVE_UNATTACHED)
  rt712->hw_init = false;

 if (status == SDW_SLAVE_ATTACHED) {
  if (rt712->hs_jack) {
   /*
 * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
 * if the device attached again, we will need to set the setting back.
 * It could avoid losing the jack detection interrupt.
 * This also could sync with the cache value as the rt712_sdca_jack_init set.
 */

   sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1,
    SDW_SCP_SDCA_INTMASK_SDCA_0);
   sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2,
    SDW_SCP_SDCA_INTMASK_SDCA_8);
  }
 }

 /*
 * Perform initialization only if slave status is present and
 * hw_init flag is false
 */

 if (rt712->hw_init || status != SDW_SLAVE_ATTACHED)
  return 0;

 /* perform I/O transfers required for Slave initialization */
 return rt712_sdca_io_init(&slave->dev, slave);
}

static int rt712_sdca_read_prop(struct sdw_slave *slave)
{
 struct sdw_slave_prop *prop = &slave->prop;
 int nval;
 int i, j;
 u32 bit;
 unsigned long addr;
 struct sdw_dpn_prop *dpn;

 sdw_slave_read_prop(slave);

 prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
 prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;

 prop->paging_support = true;

 /* first we need to allocate memory for set bits in port lists */
 prop->source_ports = BIT(8) | BIT(4); /* BITMAP: 100010000 */
 prop->sink_ports = BIT(3) | BIT(1); /* BITMAP:  00001010 */

 nval = hweight32(prop->source_ports);
 prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
  sizeof(*prop->src_dpn_prop), GFP_KERNEL);
 if (!prop->src_dpn_prop)
  return -ENOMEM;

 i = 0;
 dpn = prop->src_dpn_prop;
 addr = prop->source_ports;
 for_each_set_bit(bit, &addr, 32) {
  dpn[i].num = bit;
  dpn[i].type = SDW_DPN_FULL;
  dpn[i].simple_ch_prep_sm = true;
  dpn[i].ch_prep_timeout = 10;
  i++;
 }

 /* do this again for sink now */
 nval = hweight32(prop->sink_ports);
 prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
  sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
 if (!prop->sink_dpn_prop)
  return -ENOMEM;

 j = 0;
 dpn = prop->sink_dpn_prop;
 addr = prop->sink_ports;
 for_each_set_bit(bit, &addr, 32) {
  dpn[j].num = bit;
  dpn[j].type = SDW_DPN_FULL;
  dpn[j].simple_ch_prep_sm = true;
  dpn[j].ch_prep_timeout = 10;
  j++;
 }

 /* set the timeout values */
 prop->clk_stop_timeout = 1380;

 /* wake-up event */
 prop->wake_capable = 1;

 return 0;
}

static int rt712_sdca_interrupt_callback(struct sdw_slave *slave,
     struct sdw_slave_intr_status *status)
{
 struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
 int ret, stat;
 int count = 0, retry = 3;
 unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;

 dev_dbg(&slave->dev,
  "%s control_port_stat=%x, sdca_cascade=%x", __func__,
  status->control_port, status->sdca_cascade);

 if (cancel_delayed_work_sync(&rt712->jack_detect_work)) {
  dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
  /* avoid the HID owner doesn't change to device */
  if (rt712->scp_sdca_stat2)
   scp_sdca_stat2 = rt712->scp_sdca_stat2;
 }

 /*
 * The critical section below intentionally protects a rather large piece of code.
 * We don't want to allow the system suspend to disable an interrupt while we are
 * processing it, which could be problematic given the quirky SoundWire interrupt
 * scheme. We do want however to prevent new workqueues from being scheduled if
 * the disable_irq flag was set during system suspend.
 */

 mutex_lock(&rt712->disable_irq_lock);

 ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
 if (ret < 0)
  goto io_error;
 rt712->scp_sdca_stat1 = ret;
 ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
 if (ret < 0)
  goto io_error;
 rt712->scp_sdca_stat2 = ret;
 if (scp_sdca_stat2)
  rt712->scp_sdca_stat2 |= scp_sdca_stat2;

 do {
  /* clear flag */
  ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
  if (ret < 0)
   goto io_error;
  if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
   ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT1,
      SDW_SCP_SDCA_INTMASK_SDCA_0);
   if (ret < 0)
    goto io_error;
  }
  ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
  if (ret < 0)
   goto io_error;
  if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
   ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT2,
      SDW_SCP_SDCA_INTMASK_SDCA_8);
   if (ret < 0)
    goto io_error;
  }

  /* check if flag clear or not */
  ret = sdw_read_no_pm(rt712->slave, SDW_DP0_INT);
  if (ret < 0)
   goto io_error;
  sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;

  ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
  if (ret < 0)
   goto io_error;
  scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;

  ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
  if (ret < 0)
   goto io_error;
  scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;

  stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;

  count++;
 } while (stat != 0 && count < retry);

 if (stat)
  dev_warn(&slave->dev,
   "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
   rt712->scp_sdca_stat1, rt712->scp_sdca_stat2);

 if (status->sdca_cascade && !rt712->disable_irq)
  mod_delayed_work(system_power_efficient_wq,
   &rt712->jack_detect_work, msecs_to_jiffies(30));

 mutex_unlock(&rt712->disable_irq_lock);

 return 0;

io_error:
 mutex_unlock(&rt712->disable_irq_lock);
 pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
 return ret;
}

static const struct sdw_slave_ops rt712_sdca_slave_ops = {
 .read_prop = rt712_sdca_read_prop,
 .interrupt_callback = rt712_sdca_interrupt_callback,
 .update_status = rt712_sdca_update_status,
};

static int rt712_sdca_sdw_probe(struct sdw_slave *slave,
    const struct sdw_device_id *id)
{
 struct regmap *regmap, *mbq_regmap;

 /* Regmap Initialization */
 mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt712_sdca_mbq_regmap);
 if (IS_ERR(mbq_regmap))
  return PTR_ERR(mbq_regmap);

 regmap = devm_regmap_init_sdw(slave, &rt712_sdca_regmap);
 if (IS_ERR(regmap))
  return PTR_ERR(regmap);

 return rt712_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
}

static int rt712_sdca_sdw_remove(struct sdw_slave *slave)
{
 struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);

 if (rt712->hw_init) {
  cancel_delayed_work_sync(&rt712->jack_detect_work);
  cancel_delayed_work_sync(&rt712->jack_btn_check_work);
 }

 pm_runtime_disable(&slave->dev);

 mutex_destroy(&rt712->calibrate_mutex);
 mutex_destroy(&rt712->disable_irq_lock);

 return 0;
}

static const struct sdw_device_id rt712_sdca_id[] = {
 SDW_SLAVE_ENTRY_EXT(0x025d, 0x712, 0x3, 0x1, 0),
 SDW_SLAVE_ENTRY_EXT(0x025d, 0x713, 0x3, 0x1, 0),
 SDW_SLAVE_ENTRY_EXT(0x025d, 0x716, 0x3, 0x1, 0),
 SDW_SLAVE_ENTRY_EXT(0x025d, 0x717, 0x3, 0x1, 0),
 {},
};
MODULE_DEVICE_TABLE(sdw, rt712_sdca_id);

static int rt712_sdca_dev_suspend(struct device *dev)
{
 struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);

 if (!rt712->hw_init)
  return 0;

 cancel_delayed_work_sync(&rt712->jack_detect_work);
 cancel_delayed_work_sync(&rt712->jack_btn_check_work);

 regcache_cache_only(rt712->regmap, true);
 regcache_cache_only(rt712->mbq_regmap, true);

 return 0;
}

static int rt712_sdca_dev_system_suspend(struct device *dev)
{
 struct rt712_sdca_priv *rt712_sdca = dev_get_drvdata(dev);
 struct sdw_slave *slave = dev_to_sdw_dev(dev);
 int ret1, ret2;

 if (!rt712_sdca->hw_init)
  return 0;

 /*
 * prevent new interrupts from being handled after the
 * deferred work completes and before the parent disables
 * interrupts on the link
 */

 mutex_lock(&rt712_sdca->disable_irq_lock);
 rt712_sdca->disable_irq = true;
 ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
    SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
 ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
    SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
 mutex_unlock(&rt712_sdca->disable_irq_lock);

 if (ret1 < 0 || ret2 < 0) {
  /* log but don't prevent suspend from happening */
  dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
 }

 return rt712_sdca_dev_suspend(dev);
}

#define RT712_PROBE_TIMEOUT 5000

static int rt712_sdca_dev_resume(struct device *dev)
{
 struct sdw_slave *slave = dev_to_sdw_dev(dev);
 struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
 unsigned long time;

 if (!rt712->first_hw_init)
  return 0;

 if (!slave->unattach_request) {
  mutex_lock(&rt712->disable_irq_lock);
  if (rt712->disable_irq == true) {

   sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
   sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
   rt712->disable_irq = false;
  }
  mutex_unlock(&rt712->disable_irq_lock);
  goto regmap_sync;
 }

 time = wait_for_completion_timeout(&slave->initialization_complete,
    msecs_to_jiffies(RT712_PROBE_TIMEOUT));
 if (!time) {
  dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
  sdw_show_ping_status(slave->bus, true);

  return -ETIMEDOUT;
 }

regmap_sync:
 slave->unattach_request = 0;
 regcache_cache_only(rt712->regmap, false);
 regcache_sync(rt712->regmap);
 regcache_cache_only(rt712->mbq_regmap, false);
 regcache_sync(rt712->mbq_regmap);
 return 0;
}

static const struct dev_pm_ops rt712_sdca_pm = {
 SYSTEM_SLEEP_PM_OPS(rt712_sdca_dev_system_suspend, rt712_sdca_dev_resume)
 RUNTIME_PM_OPS(rt712_sdca_dev_suspend, rt712_sdca_dev_resume, NULL)
};

static struct sdw_driver rt712_sdca_sdw_driver = {
 .driver = {
  .name = "rt712-sdca",
  .pm = pm_ptr(&rt712_sdca_pm),
 },
 .probe = rt712_sdca_sdw_probe,
 .remove = rt712_sdca_sdw_remove,
 .ops = &rt712_sdca_slave_ops,
 .id_table = rt712_sdca_id,
};
module_sdw_driver(rt712_sdca_sdw_driver);

MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver");
MODULE_AUTHOR("Shuming Fan ");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("SND_SOC_SDCA");

Messung V0.5
C=96 H=94 G=94

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.