diff options
Diffstat (limited to 'sound/soc/codecs/rt-sdw-common.c')
-rw-r--r-- | sound/soc/codecs/rt-sdw-common.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/sound/soc/codecs/rt-sdw-common.c b/sound/soc/codecs/rt-sdw-common.c new file mode 100644 index 000000000000..ad61943ce75f --- /dev/null +++ b/sound/soc/codecs/rt-sdw-common.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt-sdw-common.c +// +// Copyright(c) 2024 Realtek Semiconductor Corp. +// + +/* + * This file defines common functions used with Realtek soundwire codecs. + */ + +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <linux/soundwire/sdw_registers.h> +#include <sound/jack.h> + +#include "rt-sdw-common.h" + +/** + * rt_sdca_index_write - Write a value to Realtek defined register. + * + * @map: map for setting. + * @nid: Realtek-defined ID. + * @reg: register. + * @value: value. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int rt_sdca_index_write(struct regmap *map, unsigned int nid, + unsigned int reg, unsigned int value) +{ + unsigned int addr = (nid << 20) | reg; + int ret; + + ret = regmap_write(map, addr, value); + if (ret < 0) + pr_err("Failed to set value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(rt_sdca_index_write); + +/** + * rt_sdca_index_read - Read value from Realtek defined register. + * + * @map: map for setting. + * @nid: Realtek-defined ID. + * @reg: register. + * @value: value. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int rt_sdca_index_read(struct regmap *map, unsigned int nid, + unsigned int reg, unsigned int *value) +{ + unsigned int addr = (nid << 20) | reg; + int ret; + + ret = regmap_read(map, addr, value); + if (ret < 0) + pr_err("Failed to get value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(rt_sdca_index_read); + +/** + * rt_sdca_index_update_bits - Update value on Realtek defined register. + * + * @map: map for setting. + * @nid: Realtek-defined ID. + * @reg: register. + * @mask: Bitmask to change + * @val: New value for bitmask + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ + +int rt_sdca_index_update_bits(struct regmap *map, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt_sdca_index_read(map, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + return rt_sdca_index_write(map, nid, reg, tmp); +} +EXPORT_SYMBOL_GPL(rt_sdca_index_update_bits); + +/** + * rt_sdca_btn_type - Decision of button type. + * + * @buffer: UMP message buffer. + * + * A button type will be returned regarding to buffer, + * it returns zero if buffer cannot be recognized. + */ +int rt_sdca_btn_type(unsigned char *buffer) +{ + u8 btn_type = 0; + int ret = 0; + + btn_type |= buffer[0] & 0xf; + btn_type |= (buffer[0] >> 4) & 0xf; + btn_type |= buffer[1] & 0xf; + btn_type |= (buffer[1] >> 4) & 0xf; + + if (btn_type & BIT(0)) + ret |= SND_JACK_BTN_2; + if (btn_type & BIT(1)) + ret |= SND_JACK_BTN_3; + if (btn_type & BIT(2)) + ret |= SND_JACK_BTN_0; + if (btn_type & BIT(3)) + ret |= SND_JACK_BTN_1; + + return ret; +} +EXPORT_SYMBOL_GPL(rt_sdca_btn_type); + +/** + * rt_sdca_headset_detect - Headset jack type detection. + * + * @map: map for setting. + * @entity_id: SDCA entity ID. + * + * A headset jack type will be returned, a negative errno will + * be returned in error cases. + */ +int rt_sdca_headset_detect(struct regmap *map, unsigned int entity_id) +{ + unsigned int det_mode, jack_type; + int ret; + + /* get detected_mode */ + ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_JACK_CODEC, entity_id, + RT_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x03: + jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + jack_type = SND_JACK_HEADSET; + break; + default: + jack_type = 0; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(map, SDW_SDCA_CTL(SDCA_NUM_JACK_CODEC, entity_id, + RT_SDCA_CTL_SELECTED_MODE, 0), det_mode); + if (ret < 0) + goto io_error; + } + + return jack_type; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL_GPL(rt_sdca_headset_detect); + +/** + * rt_sdca_button_detect - Read UMP message and decide button type. + * + * @map: map for setting. + * @entity_id: SDCA entity ID. + * @hid_buf_addr: HID buffer address. + * @hid_id: Report ID for HID. + * + * A button type will be returned regarding to buffer, + * it returns zero if buffer cannot be recognized. + */ +int rt_sdca_button_detect(struct regmap *map, unsigned int entity_id, + unsigned int hid_buf_addr, unsigned int hid_id) +{ + unsigned int btn_type = 0, offset, idx, val, owner; + unsigned char buf[3]; + int ret; + + /* get current UMP message owner */ + ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_HID, entity_id, + RT_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner); + if (ret < 0) + return 0; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + /* read UMP message offset */ + ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_HID, entity_id, + RT_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto _end_btn_det_; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(map, hid_buf_addr + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + /* Report ID for HID */ + if (buf[0] == hid_id) + btn_type = rt_sdca_btn_type(&buf[1]); + +_end_btn_det_: + /* Host is owner, so set back to device */ + if (owner == 0) + /* set owner to device */ + regmap_write(map, + SDW_SDCA_CTL(SDCA_NUM_HID, entity_id, + RT_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01); + + return btn_type; +} +EXPORT_SYMBOL_GPL(rt_sdca_button_detect); + +MODULE_DESCRIPTION("Realtek soundwire common functions"); +MODULE_AUTHOR("jack yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL"); |