All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
To: shumingf@realtek.com, broonie@kernel.org, lgirdwood@gmail.com
Cc: linux-sound@vger.kernel.org, lars@metafoo.de, flove@realtek.com,
	oder_chiou@realtek.com, jack.yu@realtek.com,
	derek.fang@realtek.com,
	Charles Keepax <ckeepax@opensource.cirrus.com>
Subject: Re: [PATCH] ASoC: rt766: add RT766/RT767 SDCA driver
Date: Mon, 20 Jul 2026 16:38:38 +0200	[thread overview]
Message-ID: <fff6c488-295d-47c3-819a-478b6dc3b24b@linux.dev> (raw)
In-Reply-To: <20260720090613.2239891-1-shumingf@realtek.com>

On 7/20/26 11:06, shumingf@realtek.com wrote:
> From: Shuming Fan <shumingf@realtek.com>
> 
> This patch adds the initial SDCA multi-function codec driver for the RT766 and RT767.
> 
> Signed-off-by: Shuming Fan <shumingf@realtek.com>

Looks mostly good, I only have minor comments, see below.


> +static bool rt766_sdca_readable_register(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4:
> +	case RT766_VERSION_ID ... RT766_BOND_LATCH_ID:
> +	case 0xc344 ... 0xc345:
> +	case 0xc900:
> +	case 0xc920:
> +	case 0xd540 ... 0xd542:
> +	case 0xf01e:
> +	case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1:
> +	case 0x310100:
> +	case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END:
> +	case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END:
> +	case RT766_MUTE_REG(UAJ, USER_FU41, 1):
> +	case RT766_MUTE_REG(UAJ, USER_FU41, 2):
> +	case RT766_VOLUME_REG(UAJ, USER_FU41, 1):
> +	case RT766_VOLUME_REG(UAJ, USER_FU41, 2):
> +	case RT766_MUTE_REG(UAJ, USER_FU36, 1):
> +	case RT766_MUTE_REG(UAJ, USER_FU36, 2):
> +	case RT766_VOLUME_REG(UAJ, USER_FU36, 1):
> +	case RT766_VOLUME_REG(UAJ, USER_FU36, 2):
> +	case RT766_PDE_REQ_REG(UAJ, PDE47):
> +	case RT766_PDE_REQ_REG(UAJ, PDE34):
> +	case RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX):
> +	case RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX):
> +	/* 0x40480000 */
> +	case RT766_FUNC_STATUS_REG(UAJ):
> +	/* 0x40481400 */
> +	case RT766_PDE_ACTUAL_REG(UAJ, PDE47):
> +	/* 0x40481480 */

these single line registers listed in comments confuse me, not clear for
example if this one applies to the following line...

> +	case RT766_PDE_ACTUAL_REG(UAJ, PDE34):

but then why not adde the values for the next 3?

> +	case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1):
> +	case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2):
> +	case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE):
> +	/* 0x40600490 */
> +	case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE):
> +	case RT766_PDE_REQ_REG(MIC, PDE11):
> +	case RT766_MUTE_REG(MIC, USER_FU113, 1):
> +	case RT766_MUTE_REG(MIC, USER_FU113, 2):
> +	case RT766_MUTE_REG(MIC, USER_FU113, 3):
> +	case RT766_MUTE_REG(MIC, USER_FU113, 4):
> +	case RT766_VOLUME_REG(MIC, USER_FU113, 1):
> +	case RT766_VOLUME_REG(MIC, USER_FU113, 2):
> +	case RT766_VOLUME_REG(MIC, USER_FU113, 3):
> +	case RT766_VOLUME_REG(MIC, USER_FU113, 4):
> +	/* 0x40880000 */
> +	case RT766_FUNC_STATUS_REG(MIC):

same here, are 0x40880000  and RT766_FUNC_STATUS_REG equivalent? If yes
maybe move the comment to the right side?

> +	case RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX):
> +	/* 0x40881500 */
> +	case RT766_PDE_ACTUAL_REG(MIC, PDE11):
> +	/* 0x40c80000 */
> +	case RT766_FUNC_STATUS_REG(HID):
> +	/* 0x40c80080 - 0x40c80098 */
> +	case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ...
> +		RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH):
> +	case RT766_MUTE_REG(AMP, USER_FU21, 1):
> +	case RT766_MUTE_REG(AMP, USER_FU21, 2):
> +	case RT766_VOLUME_REG(AMP, USER_FU21, 1):
> +	case RT766_VOLUME_REG(AMP, USER_FU21, 2):
> +	case RT766_PDE_REQ_REG(AMP, PDE23):
> +	/* 0x41080000 */
> +	case RT766_FUNC_STATUS_REG(AMP):
> +	case RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER):
> +	case RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX):
> +	/* 0x41081980 */
> +	case RT766_PDE_ACTUAL_REG(AMP, PDE23):
> +	case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}

> +static const struct reg_default rt766_sdca_defaults[] = {
> +	/* 0x40400289 - 0x4040028a */
> +	{ RT766_MUTE_REG(UAJ, USER_FU41, 1), 0x01 },
> +	{ RT766_MUTE_REG(UAJ, USER_FU41, 2), 0x01 },

things are much clearer/organized here!

> +	/* 0x40400291 - 0x40400292 */
> +	{ RT766_VOLUME_REG(UAJ, USER_FU41, 1), 0x0000 },
> +	{ RT766_VOLUME_REG(UAJ, USER_FU41, 2), 0x0000 },
> +	/* 0x40400789 - 0x4040078a */
> +	{ RT766_MUTE_REG(UAJ, USER_FU36, 1), 0x01 },
> +	{ RT766_MUTE_REG(UAJ, USER_FU36, 2), 0x01 },
> +	/* 0x40400791 - 0x40400792 */
> +	{ RT766_VOLUME_REG(UAJ, USER_FU36, 1), 0x0000 },
> +	{ RT766_VOLUME_REG(UAJ, USER_FU36, 2), 0x0000 },
> +	/* 0x40401408 */
> +	{ RT766_PDE_REQ_REG(UAJ, PDE47), 0x03 },
> +	/* 0x40401488 */
> +	{ RT766_PDE_REQ_REG(UAJ, PDE34), 0x03 },
> +	/* 0x40480080 */
> +	{ RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 },
> +	/* 0x40480880 */
> +	{ RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 },
> +	/* 0x40600259 - 0x4060025a */
> +	{ RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), 0xfe00 },
> +	{ RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), 0xfe00 },
> +	/* 0x40600488 */
> +	{ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), 0x00 },

> +static struct sdca_entity *rt766_find_entity_by_label(struct sdca_function_data *func,
> +	const char *label)
> +{
> +	struct sdca_entity *entity = NULL;

useless initialization?

> +	int idx;
> +
> +	for (idx = 0; idx < func->num_entities; idx++) {
> +		entity = &func->entities[idx];
> +
> +		if (!strcmp(entity->label, label))
> +			return entity;
> +	}
> +
> +	return NULL;
> +}

> +static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766,
> +							  struct sdca_function_data *function,
> +							  struct snd_soc_component *component,
> +							  struct sdca_interrupt_info *info,
> +							  bool enabled)
> +{
> +	struct device *dev = &rt766->slave->dev;
> +	struct sdca_interrupt *interrupt;
> +	struct sdca_control *control;
> +	struct sdca_entity *entity;
> +	irq_handler_t handler;
> +	int i, j, irq, ret;
> +
> +	for (i = 0; i < function->num_entities; i++) {
> +		entity = &function->entities[i];
> +
> +		for (j = 0; j < entity->num_controls; j++) {
> +			control = &entity->controls[j];
> +			irq = control->interrupt_position;
> +
> +			switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
> +			case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
> +				handler = rt766_sdca_irq_jd_handler;
> +				break;
> +			case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
> +				handler = rt766_sdca_irq_btn_handler;
> +				break;
> +			default:
> +				continue;
> +			}
> +
> +			interrupt = &info->irqs[irq];
> +
> +			if (enabled) {
> +				ret = sdca_irq_data_populate(dev, rt766->regmap, component,
> +								function, entity, control,
> +								interrupt);
> +				if (ret)
> +					return ret;
> +
> +				interrupt->priv = rt766;
> +				ret = sdca_irq_request(dev, info, irq, interrupt->name,
> +								handler, interrupt);

There was a proposal from Charles to do an irq_populate_early, is there
a conflict between the two patchsets? see "ASoC: SDCA: Populate IRQ data
earlier"

> +				if (ret) {
> +					dev_err(dev, "failed to request irq %s: %d\n",
> +						interrupt->name, ret);
> +					return ret;
> +				}
> +				dev_dbg(dev, "Requesting IRQ %d InterruptName=%s\n", irq, interrupt->name);
> +			} else {
> +				sdca_irq_free(dev, info, irq, interrupt->name, interrupt);
> +				dev_dbg(dev, "Freeing IRQ %d\n", irq);
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}

> +static int rt766_sdca_set_fu41_playback_ctl(struct rt766_sdca_priv *rt766)
> +{
> +	int err;
> +	unsigned int ch_01, ch_02;

nit-pick: likely copy/paste from other drivers, but flipping the two
lines would be nicer. there are a couple of similar patterns in this driver.

> +
> +	ch_01 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_l_mute) ? 0x01 : 0x00;
> +	ch_02 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_r_mute) ? 0x01 : 0x00;
> +
> +	err = regmap_write(rt766->regmap, RT766_MUTE_REG(UAJ, USER_FU41, 1), ch_01);
> +	if (err < 0)
> +		return err;
> +
> +	err = regmap_write(rt766->regmap, RT766_MUTE_REG(UAJ, USER_FU41, 2), ch_02);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}

> +static int rt766_sdca_pde47_event(struct snd_soc_dapm_widget *w,
> +	struct snd_kcontrol *kcontrol, int event)
> +{
> +	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
> +	struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component);
> +	const struct sdca_entity *entity = NULL;

useless init?

> +	unsigned char ps0 = 0x0, ps3 = 0x3;
> +	int from_ps, to_ps;
> +	int ret;
> +
> +	switch (event) {
> +	case SND_SOC_DAPM_POST_PMU:
> +		regmap_write(rt766->regmap, RT766_PDE_REQ_REG(UAJ, PDE47), ps0);
> +		from_ps = ps3;
> +		to_ps = ps0;
> +		break;
> +	case SND_SOC_DAPM_PRE_PMD:
> +		regmap_write(rt766->regmap, RT766_PDE_REQ_REG(UAJ, PDE47), ps3);
> +		from_ps = ps0;
> +		to_ps = ps3;
> +		break;
> +	}
> +
> +	entity = rt766_find_entity_by_label(rt766->uaj_func_data, "PDE 47");
> +	ret = sdca_asoc_pde_poll_actual_ps(component->dev, rt766->regmap,
> +				   RT766_FUNC_NUM_UAJ,
> +				   RT766_SDCA_ENT_PDE47,
> +				   from_ps, to_ps,
> +				   entity ? entity->pde.max_delay : NULL,
> +				   entity ? entity->pde.num_max_delay : 0);
> +	if (ret)
> +		dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n",
> +			__func__, from_ps, to_ps, ret);
> +
> +	return ret;
> +}

> +static int rt766_sdca_pde34_event(struct snd_soc_dapm_widget *w,
> +	struct snd_kcontrol *kcontrol, int event)
> +{
> +	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
> +	struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component);
> +	const struct sdca_entity *entity = NULL;

useless init? Same comment for all PDE events.

> +	unsigned char ps0 = 0x0, ps3 = 0x3;
> +	int from_ps, to_ps;
> +	int ret;
> +
> +	switch (event) {
> +	case SND_SOC_DAPM_POST_PMU:
> +		regmap_write(rt766->regmap, RT766_PDE_REQ_REG(UAJ, PDE34), ps0);
> +		from_ps = ps3;
> +		to_ps = ps0;
> +		break;
> +	case SND_SOC_DAPM_PRE_PMD:
> +		regmap_write(rt766->regmap, RT766_PDE_REQ_REG(UAJ, PDE34), ps3);
> +		from_ps = ps0;
> +		to_ps = ps3;
> +		break;
> +	}
> +
> +	entity = rt766_find_entity_by_label(rt766->uaj_func_data, "PDE 34");
> +	ret = sdca_asoc_pde_poll_actual_ps(component->dev, rt766->regmap,
> +				   RT766_FUNC_NUM_UAJ,
> +				   RT766_SDCA_ENT_PDE34,
> +				   from_ps, to_ps,
> +				   entity ? entity->pde.max_delay : NULL,
> +				   entity ? entity->pde.num_max_delay : 0);
> +	if (ret)
> +		dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n",
> +			__func__, from_ps, to_ps, ret);
> +
> +	return ret;
> +}


> +static int rt766_sdca_probe(struct snd_soc_component *component)
> +{
> +	struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component);
> +	struct device *dev = &rt766->slave->dev;
> +	int ret;
> +
> +	rt766->component = component;
> +
> +	ret = pm_runtime_resume(component->dev);
> +	if (ret < 0 && ret != -EACCES)
> +		return ret;
> +
> +	if (rt766->uaj_func_data) {
> +		dev_dbg(dev, "%s : irq %d\n", __func__, rt766->slave->irq);
> +
> +		rt766->irq_info = sdca_irq_allocate(dev, rt766->regmap, rt766->slave->irq);
> +		if (IS_ERR(rt766->irq_info))
> +			return PTR_ERR(rt766->irq_info);
> +
> +		ret = rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data,
> +			component, rt766->irq_info, true);

same question on IRQ allocate/request for Charles.

> +		if (ret < 0) {
> +			dev_err(dev, "Failed to request UAJ SDCA IRQ: %d\n", ret);
> +			return ret;
> +		}
> +
> +		if (rt766->hid_func_data) {
> +			ret = rt766_sdca_irq_ctl(rt766, rt766->hid_func_data,
> +				component, rt766->irq_info, true);
> +			if (ret < 0) {
> +				dev_err(dev, "Failed to request HID SDCA IRQ: %d\n", ret);
> +				return ret;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}

> +static unsigned int rt766_find_dt_rates(struct device *dev, struct sdca_function_data *function,
> +							const char *label)
> +{
> +	struct sdca_entity *entity = NULL;

useless init?

> +	unsigned int rates;
> +	int i, ret;
> +
> +	for (i = 0; i < function->num_entities; i++) {
> +		entity = &function->entities[i];
> +
> +		if (strcmp(entity->label, label))
> +			continue;
> +
> +		/* Can't check earlier as only terminals have an iot member. */
> +		if (!entity->iot.is_dataport)
> +			continue;
> +
> +		ret = rt766_parse_rates(dev, function, entity, &rates);
> +		if (ret < 0) {
> +			dev_dbg(dev, "%s: failed to parse rates for entity %s\n",
> +				__func__, entity->label);
> +			return 0;
> +		}
> +
> +		dev_dbg(dev, "%s: %s supports rates 0x%08x\n", __func__, entity->label, rates);
> +	}
> +
> +	return rates;
> +}
> +
> +int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave)
> +{
> +	struct sdca_function_data *func_data_ptr;
> +	struct rt766_sdca_priv *rt766;
> +	unsigned int rates;
> +	int ret;
> +	int i;
> +
> +	rt766 = devm_kzalloc(dev, sizeof(*rt766), GFP_KERNEL);
> +	if (!rt766)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, rt766);
> +	rt766->slave = slave;
> +	rt766->regmap = regmap;
> +
> +	regcache_cache_only(rt766->regmap, true);
> +
> +	mutex_init(&rt766->disable_irq_lock);
> +
> +	/*
> +	 * Mark hw_init to false
> +	 * HW init will be performed when device reports present
> +	 */
> +	rt766->hw_init = false;
> +	rt766->first_hw_init = false;
> +	rt766->fu41_dapm_mute = true;
> +	rt766->fu41_mixer_l_mute = rt766->fu41_mixer_r_mute = false;
> +	rt766->fu36_dapm_mute = true;
> +	rt766->fu36_mixer_l_mute = rt766->fu36_mixer_r_mute = true;
> +	rt766->fu21_dapm_mute = true;
> +	rt766->fu21_mixer_l_mute = rt766->fu21_mixer_r_mute = false;
> +	rt766->fu113_dapm_mute = true;
> +	rt766->fu113_mixer_mute[0] = rt766->fu113_mixer_mute[1] =
> +		rt766->fu113_mixer_mute[2] = rt766->fu113_mixer_mute[3] = true;
> +
> +	/* get SDCA function data */
> +	dev_dbg(dev, "SDCA functions found: %d", slave->sdca_data.num_functions);
> +	for (i = 0; i < slave->sdca_data.num_functions; i++) {
> +		func_data_ptr = devm_kzalloc(dev, sizeof(*func_data_ptr), GFP_KERNEL);
> +		if (!func_data_ptr)
> +			return dev_err_probe(dev, -ENOMEM, "failed to allocate function data");
> +
> +		func_data_ptr->desc = &slave->sdca_data.function[i];
> +		ret = sdca_parse_function(dev, slave, func_data_ptr);
> +		if (ret) {
> +			devm_kfree(dev, func_data_ptr);
> +			return ret;
> +		}
> +		dev_dbg(dev, "Function type=%d, num_entities=%d",
> +			slave->sdca_data.function[i].type, func_data_ptr->num_entities);
> +
> +		switch (slave->sdca_data.function[i].type) {
> +		case SDCA_FUNCTION_TYPE_UAJ:
> +			rt766->uaj_func_data = func_data_ptr;
> +			/*
> +			 * Some machines may only support a subset of the sample rates supported by the codec.
> +			 * Therefore, we need to parse the supported sample rates from the DisCo table and
> +			 * configure them in the DAI. If the DisCo table does not provide sample rate information,
> +			 * we will fall back to the default supported rates defined in the codec driver.
> +			 */
> +			rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 41");
> +			if (rates)
> +				rt766_sdca_dai[0].playback.rates = rates;
> +
> +			rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 36");
> +			if (rates)
> +				rt766_sdca_dai[0].capture.rates = rates;
> +			break;
> +		case SDCA_FUNCTION_TYPE_SMART_AMP:
> +			rt766->sa_func_data = func_data_ptr;
> +			rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 21");
> +			if (rates)
> +				rt766_sdca_dai[1].playback.rates = rates;
> +			break;
> +		case SDCA_FUNCTION_TYPE_SMART_MIC:
> +			rt766->sm_func_data = func_data_ptr;
> +			rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 113");
> +			if (rates)
> +				rt766_sdca_dai[2].capture.rates = rates;

are the sdca_dai[index] related to the function? Wondering if we can use
e.g sdca_dai[MIC] instead?

> +			break;
> +		case SDCA_FUNCTION_TYPE_HID:
> +			rt766->hid_func_data = func_data_ptr;
> +			break;
> +		default:
> +			dev_dbg(dev, "Unexpected SDCA function type found: %d",
> +				slave->sdca_data.function[i].type);
> +		}
> +	}
> +
> +	ret =  devm_snd_soc_register_component(dev,
> +			&soc_sdca_dev_rt766, rt766_sdca_dai, ARRAY_SIZE(rt766_sdca_dai));
> +	if (ret < 0)
> +		return ret;
> +
> +	/* set autosuspend parameters */
> +	pm_runtime_set_autosuspend_delay(dev, 3000);
> +	pm_runtime_use_autosuspend(dev);
> +
> +	/* make sure the device does not suspend immediately */
> +	pm_runtime_mark_last_busy(dev);
> +
> +	pm_runtime_enable(dev);
> +
> +	dev_dbg(dev, "%s\n", __func__);
> +
> +	return 0;
> +}



  reply	other threads:[~2026-07-20 14:45 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-20  9:06 [PATCH] ASoC: rt766: add RT766/RT767 SDCA driver shumingf
2026-07-20 14:38 ` Pierre-Louis Bossart [this message]
2026-07-21  7:09   ` Shuming [范書銘]
2026-07-20 16:21 ` Charles Keepax
2026-07-21  7:10   ` Shuming [范書銘]
2026-07-21 10:29     ` Charles Keepax

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=fff6c488-295d-47c3-819a-478b6dc3b24b@linux.dev \
    --to=pierre-louis.bossart@linux.dev \
    --cc=broonie@kernel.org \
    --cc=ckeepax@opensource.cirrus.com \
    --cc=derek.fang@realtek.com \
    --cc=flove@realtek.com \
    --cc=jack.yu@realtek.com \
    --cc=lars@metafoo.de \
    --cc=lgirdwood@gmail.com \
    --cc=linux-sound@vger.kernel.org \
    --cc=oder_chiou@realtek.com \
    --cc=shumingf@realtek.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.