| // 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"); |