/* Change DM_CON/DP_CON/VBUSIN switch according to cable type */ staticint rt8973a_muic_set_path(struct rt8973a_muic_info *info, unsignedint con_sw, bool attached)
{ int ret;
/* *Don'tneedtoseth/wpathaccordingtocabletype *ifAuto-configurationmodeofCONTROL1registeristrue.
*/ if (info->auto_config) return0;
if (!attached)
con_sw = DM_DP_SWITCH_UART;
switch (con_sw) { case DM_DP_SWITCH_OPEN: case DM_DP_SWITCH_USB: case DM_DP_SWITCH_UART:
ret = regmap_update_bits(info->regmap, RT8973A_REG_MANUAL_SW1,
RT8973A_REG_MANUAL_SW1_DP_MASK |
RT8973A_REG_MANUAL_SW1_DM_MASK,
con_sw); if (ret < 0) {
dev_err(info->dev, "cannot update DM_CON/DP_CON switch\n"); return ret;
} break; default:
dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n",
con_sw); return -EINVAL;
}
/* Read ADC value according to external cable or button */
ret = regmap_read(info->regmap, RT8973A_REG_ADC, &adc); if (ret) {
dev_err(info->dev, "failed to read ADC register\n"); return ret;
}
cable_type = adc & RT8973A_REG_ADC_MASK;
/* Read Device 1 reigster to identify correct cable type */
ret = regmap_read(info->regmap, RT8973A_REG_DEV1, &dev1); if (ret) {
dev_err(info->dev, "failed to read DEV1 register\n"); return ret;
}
switch (event) { case RT8973A_EVENT_ATTACH:
cable_type = rt8973a_muic_get_cable_type(info);
attached = true; break; case RT8973A_EVENT_DETACH:
cable_type = prev_cable_type;
attached = false; break; case RT8973A_EVENT_OVP: case RT8973A_EVENT_OTP:
dev_warn(info->dev, "happen Over %s issue. Need to disconnect all cables\n",
event == RT8973A_EVENT_OVP ? "Voltage" : "Temperature");
cable_type = prev_cable_type;
attached = false; break; default:
dev_err(info->dev, "Cannot handle this event (event:%d)\n", event); return -EINVAL;
}
prev_cable_type = cable_type;
switch (cable_type) { case RT8973A_MUIC_ADC_OTG:
id = EXTCON_USB_HOST;
con_sw = DM_DP_SWITCH_USB; break; case RT8973A_MUIC_ADC_TA:
id = EXTCON_CHG_USB_DCP;
con_sw = DM_DP_SWITCH_OPEN; break; case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB: case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB:
id = EXTCON_JIG;
con_sw = DM_DP_SWITCH_USB; break; case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART: case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART:
id = EXTCON_JIG;
con_sw = DM_DP_SWITCH_UART; break; case RT8973A_MUIC_ADC_USB:
id = EXTCON_USB;
con_sw = DM_DP_SWITCH_USB; break; case RT8973A_MUIC_ADC_OPEN: return0; case RT8973A_MUIC_ADC_UNKNOWN_ACC_1: case RT8973A_MUIC_ADC_UNKNOWN_ACC_2: case RT8973A_MUIC_ADC_UNKNOWN_ACC_3: case RT8973A_MUIC_ADC_UNKNOWN_ACC_4: case RT8973A_MUIC_ADC_UNKNOWN_ACC_5:
dev_warn(info->dev, "Unknown accessory type (adc:0x%x)\n", cable_type); return0; case RT8973A_MUIC_ADC_AUDIO_SEND_END_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S1_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S2_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S3_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S4_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S5_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S6_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S7_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S8_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S9_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S10_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S11_BUTTON: case RT8973A_MUIC_ADC_AUDIO_REMOTE_S12_BUTTON: case RT8973A_MUIC_ADC_AUDIO_TYPE2:
dev_warn(info->dev, "Audio device/button type (adc:0x%x)\n", cable_type); return0; case RT8973A_MUIC_ADC_RESERVED_ACC_1: case RT8973A_MUIC_ADC_RESERVED_ACC_2: case RT8973A_MUIC_ADC_RESERVED_ACC_3: case RT8973A_MUIC_ADC_RESERVED_ACC_4: case RT8973A_MUIC_ADC_RESERVED_ACC_5: case RT8973A_MUIC_ADC_PHONE_POWERED_DEV: return0; default:
dev_err(info->dev, "Cannot handle this cable_type (adc:0x%x)\n",
cable_type); return -EINVAL;
}
/* Change internal hardware path(DM_CON/DP_CON) */
ret = rt8973a_muic_set_path(info, con_sw, attached); if (ret < 0) return ret;
/* Change the state of external accessory */
extcon_set_state_sync(info->edev, id, attached); if (id == EXTCON_USB)
extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
attached);
return0;
}
staticvoid rt8973a_muic_irq_work(struct work_struct *work)
{ struct rt8973a_muic_info *info = container_of(work, struct rt8973a_muic_info, irq_work); int ret = 0;
if (!info->edev) return;
mutex_lock(&info->mutex);
/* Detect attached or detached cables */ if (info->irq_attach) {
ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_ATTACH);
info->irq_attach = false;
}
if (info->irq_detach) {
ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_DETACH);
info->irq_detach = false;
}
if (info->irq_ovp) {
ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_OVP);
info->irq_ovp = false;
}
if (info->irq_otp) {
ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_OTP);
info->irq_otp = false;
}
if (ret < 0)
dev_err(info->dev, "failed to handle MUIC interrupt\n");
mutex_unlock(&info->mutex);
}
static irqreturn_t rt8973a_muic_irq_handler(int irq, void *data)
{ struct rt8973a_muic_info *info = data; int i, irq_type = -1;
for (i = 0; i < info->num_muic_irqs; i++) if (irq == info->muic_irqs[i].virq)
irq_type = info->muic_irqs[i].irq;
switch (irq_type) { case RT8973A_INT1_ATTACH:
info->irq_attach = true; break; case RT8973A_INT1_DETACH:
info->irq_detach = true; break; case RT8973A_INT1_OVP:
info->irq_ovp = true; break; case RT8973A_INT1_OTP:
info->irq_otp = true; break; case RT8973A_INT1_CHGDET: case RT8973A_INT1_DCD_T: case RT8973A_INT1_CONNECT: case RT8973A_INT1_ADC_CHG: case RT8973A_INT2_UVLO: case RT8973A_INT2_POR: case RT8973A_INT2_OTP_FET: case RT8973A_INT2_OVP_FET: case RT8973A_INT2_OCP_LATCH: case RT8973A_INT2_OCP: case RT8973A_INT2_OVP_OCP: default:
dev_dbg(info->dev, "Cannot handle this interrupt (%d)\n", irq_type); break;
}
/* Notify the state of connector cable or not */
ret = rt8973a_muic_cable_handler(info, RT8973A_EVENT_ATTACH); if (ret < 0)
dev_warn(info->dev, "failed to detect cable state\n");
}
staticvoid rt8973a_init_dev_type(struct rt8973a_muic_info *info)
{ unsignedint data, vendor_id, version_id; int i, ret;
/* To test I2C, Print version_id and vendor_id of RT8973A */
ret = regmap_read(info->regmap, RT8973A_REG_DEVICE_ID, &data); if (ret) {
dev_err(info->dev, "failed to read DEVICE_ID register: %d\n", ret); return;
}
/* Initiazle the register of RT8973A device to bring-up */ for (i = 0; i < info->num_reg_data; i++) {
u8 reg = info->reg_data[i].reg;
u8 mask = info->reg_data[i].mask;
u8 val = 0;
if (info->reg_data[i].invert)
val = ~info->reg_data[i].val; else
val = info->reg_data[i].val;
/* Check whether RT8973A is auto switching mode or not */
ret = regmap_read(info->regmap, RT8973A_REG_CONTROL1, &data); if (ret) {
dev_err(info->dev, "failed to read CONTROL1 register: %d\n", ret); return;
}
data &= RT8973A_REG_CONTROL1_AUTO_CONFIG_MASK; if (data) {
info->auto_config = true;
dev_info(info->dev, "Enable Auto-configuration for internal path\n");
}
}
staticint rt8973a_muic_i2c_probe(struct i2c_client *i2c)
{ struct device_node *np = i2c->dev.of_node; struct rt8973a_muic_info *info; int i, ret, irq_flags;
if (!np) return -EINVAL;
info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM;
i2c_set_clientdata(i2c, info);
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.