108 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * UAC3 Power Domain state management functions4 */5 6#include <linux/slab.h>7#include <linux/usb.h>8#include <linux/usb/audio.h>9#include <linux/usb/audio-v2.h>10#include <linux/usb/audio-v3.h>11 12#include "usbaudio.h"13#include "helper.h"14#include "power.h"15 16struct snd_usb_power_domain *17snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface,18 unsigned char id)19{20 struct snd_usb_power_domain *pd;21 void *p;22 23 pd = kzalloc(sizeof(*pd), GFP_KERNEL);24 if (!pd)25 return NULL;26 27 p = NULL;28 while ((p = snd_usb_find_csint_desc(ctrl_iface->extra,29 ctrl_iface->extralen,30 p, UAC3_POWER_DOMAIN)) != NULL) {31 struct uac3_power_domain_descriptor *pd_desc = p;32 int i;33 34 if (!snd_usb_validate_audio_desc(p, UAC_VERSION_3))35 continue;36 for (i = 0; i < pd_desc->bNrEntities; i++) {37 if (pd_desc->baEntityID[i] == id) {38 pd->pd_id = pd_desc->bPowerDomainID;39 pd->pd_d1d0_rec =40 le16_to_cpu(pd_desc->waRecoveryTime1);41 pd->pd_d2d0_rec =42 le16_to_cpu(pd_desc->waRecoveryTime2);43 pd->ctrl_iface = ctrl_iface;44 return pd;45 }46 }47 }48 49 kfree(pd);50 return NULL;51}52 53int snd_usb_power_domain_set(struct snd_usb_audio *chip,54 struct snd_usb_power_domain *pd,55 unsigned char state)56{57 struct usb_device *dev = chip->dev;58 unsigned char current_state;59 int err, idx;60 61 idx = snd_usb_ctrl_intf(pd->ctrl_iface) | (pd->pd_id << 8);62 63 err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),64 UAC2_CS_CUR,65 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,66 UAC3_AC_POWER_DOMAIN_CONTROL << 8, idx,67 ¤t_state, sizeof(current_state));68 if (err < 0) {69 dev_err(&dev->dev, "Can't get UAC3 power state for id %d\n",70 pd->pd_id);71 return err;72 }73 74 if (current_state == state) {75 dev_dbg(&dev->dev, "UAC3 power domain id %d already in state %d\n",76 pd->pd_id, state);77 return 0;78 }79 80 err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC2_CS_CUR,81 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,82 UAC3_AC_POWER_DOMAIN_CONTROL << 8, idx,83 &state, sizeof(state));84 if (err < 0) {85 dev_err(&dev->dev, "Can't set UAC3 power state to %d for id %d\n",86 state, pd->pd_id);87 return err;88 }89 90 if (state == UAC3_PD_STATE_D0) {91 switch (current_state) {92 case UAC3_PD_STATE_D2:93 udelay(pd->pd_d2d0_rec * 50);94 break;95 case UAC3_PD_STATE_D1:96 udelay(pd->pd_d1d0_rec * 50);97 break;98 default:99 return -EINVAL;100 }101 }102 103 dev_dbg(&dev->dev, "UAC3 power domain id %d change to state %d\n",104 pd->pd_id, state);105 106 return 0;107}108