/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __USB_TYPEC_ALTMODE_H
#define __USB_TYPEC_ALTMODE_H
#include <linux/mod_devicetable.h>
#include <linux/usb/typec.h>
#include <linux/device.h>
#define MODE_DISCOVERY_MAX 6
struct typec_altmode_ops;
/**
* struct typec_altmode - USB Type-C alternate mode device
* @dev: Driver model's view of this device
* @svid: Standard or Vendor ID (SVID) of the alternate mode
* @mode: Index of the Mode
* @vdo: VDO returned by Discover Modes USB PD command
* @active: Tells has the mode been entered or not
* @desc: Optional human readable description of the mode
* @ops: Operations vector from the driver
* @cable_ops: Cable operations vector from the driver.
*/
struct typec_altmode {
struct device dev;
u16 svid;
int mode;
u32 vdo;
unsigned int active:1;
char *desc;
const struct typec_altmode_ops *ops;
const struct typec_cable_ops *cable_ops;
};
#define to_typec_altmode(d) container_of(d, struct typec_altmode, dev)
static inline void typec_altmode_set_drvdata(struct typec_altmode *altmode,
void *data)
{
dev_set_drvdata(&altmode->dev, data);
}
static inline void *typec_altmode_get_drvdata(struct typec_altmode *altmode)
{
return dev_get_drvdata(&altmode->dev);
}
/**
* struct typec_altmode_ops - Alternate mode specific operations vector
* @enter: Operations to be executed with Enter Mode Command
* @exit: Operations to be executed with Exit Mode Command
* @attention: Callback for Attention Command
* @vdm: Callback for SVID specific commands
* @notify: Communication channel for platform and the alternate mode
* @activate: User callback for Enter/Exit Mode
*/
struct typec_altmode_ops {
int (*enter)(struct typec_altmode *altmode, u32 *vdo);
int (*exit)(struct typec_altmode *altmode);
void (*attention)(struct typec_altmode *altmode, u32 vdo);
int (*vdm)(struct typec_altmode *altmode, const u32 hdr,
const u32 *vdo, int cnt);
int (*notify)(struct typec_altmode *altmode, unsigned long conf,
void *data);
int (*activate)(struct typec_altmode *altmode, int activate);
};
int typec_altmode_enter(struct typec_altmode *altmode, u32 *vdo);
int typec_altmode_exit(struct typec_altmode *altmode);
int typec_altmode_attention(struct typec_altmode *altmode, u32 vdo);
int typec_altmode_vdm(struct typec_altmode *altmode,
const u32 header, const u32 *vdo, int count);
int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf,
void *data);
const struct typec_altmode *
typec_altmode_get_partner(struct typec_altmode *altmode);
/**
* struct typec_cable_ops - Cable alternate mode operations vector
* @enter: Operations to be executed with Enter Mode Command
* @exit: Operations to be executed with Exit Mode Command
* @vdm: Callback for SVID specific commands
*/
struct typec_cable_ops {
int (*enter)(struct typec_altmode *altmode, enum typec_plug_index sop, u32 *vdo);
int (*exit)(struct typec_altmode *altmode, enum typec_plug_index sop);
int (*vdm)(struct typec_altmode *altmode, enum typec_plug_index sop,
const u32 hdr, const u32 *vdo, int cnt);
};
int typec_cable_altmode_enter(struct typec_altmode *altmode, enum typec_plug_index sop, u32 *vdo);
int typec_cable_altmode_exit(struct typec_altmode *altmode, enum typec_plug_index sop);
int typec_cable_altmode_vdm(struct typec_altmode *altmode, enum typec_plug_index sop,
const u32 header, const u32 *vdo, int count);
/**
* typec_altmode_get_cable_svdm_version - Get negotiated SVDM version for cable plug
* @altmode: Handle to the alternate mode
*/
static inline int
typec_altmode_get_cable_svdm_version(struct typec_altmode *altmode)
{
return typec_get_cable_svdm_version(typec_altmode2port(altmode));
}
/*
* These are the connector states (USB, Safe and Alt Mode) defined in USB Type-C
* Specification. SVID specific connector states are expected to follow and
* start from the value TYPEC_STATE_MODAL.
*/
enum {
TYPEC_STATE_SAFE, /* USB Safe State */
TYPEC_STATE_USB, /* USB Operation */
TYPEC_STATE_MODAL, /* Alternate Modes */
};
/*
* For the muxes there is no difference between Accessory Modes and Alternate
* Modes, so the Accessory Modes are supplied with specific modal state values
* here. Unlike with Alternate Modes, where the mux will be linked with the
* alternate mode device, the mux for Accessory Modes will be linked with the
* port device instead.
*
* Port drivers can use TYPEC_MODE_AUDIO and TYPEC_MODE_DEBUG as the mode
* value for typec_set_mode() when accessory modes are supported.
*
* USB4 also requires that the pins on the connector are repurposed, just like
* Alternate Modes. USB4 mode is however not entered with the Enter Mode Command
* like the Alternate Modes are, but instead with a special Enter_USB Message.
* The Enter_USB Message can also be used for setting to connector to operate in
* USB 3.2 or in USB 2.0 mode instead of USB4.
*
* The Enter_USB specific "USB Modes" are also supplied here as special modal
* state values, just like the Accessory Modes.
*/
enum {
TYPEC_MODE_USB2 = TYPEC_STATE_MODAL, /* USB 2.0 mode */
TYPEC_MODE_USB3, /* USB 3.2 mode */
TYPEC_MODE_USB4, /* USB4 mode */
TYPEC_MODE_AUDIO, /* Audio Accessory */
TYPEC_MODE_DEBUG, /* Debug Accessory */
};
#define TYPEC_MODAL_STATE(_state_) ((_state_) + TYPEC_STATE_MODAL)
struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *altmode,
enum typec_plug_index index);
void typec_altmode_put_plug(struct typec_altmode *plug);
struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes,
size_t n, u16 svid, u8 mode);
/**
* typec_altmode_get_orientation - Get cable plug orientation
* @altmode: Handle to the alternate mode
*/
static inline enum typec_orientation
typec_altmode_get_orientation(struct typec_altmode *altmode)
{
return typec_get_orientation(typec_altmode2port(altmode));
}
/**
* typec_altmode_get_svdm_version - Get negotiated SVDM version
* @altmode: Handle to the alternate mode
*/
static inline int
typec_altmode_get_svdm_version(struct typec_altmode *altmode)
{
return typec_get_negotiated_svdm_version(typec_altmode2port(altmode));
}
/**
* struct typec_altmode_driver - USB Type-C alternate mode device driver
* @id_table: Null terminated array of SVIDs
* @probe: Callback for device binding
* @remove: Callback for device unbinding
* @driver: Device driver model driver
*
* These drivers will be bind to the partner alternate mode devices. They will
* handle all SVID specific communication.
*/
struct typec_altmode_driver {
const struct typec_device_id *id_table;
int (*probe)(struct typec_altmode *altmode);
void (*remove)(struct typec_altmode *altmode);
struct device_driver driver;
};
#define to_altmode_driver(d) container_of(d, struct typec_altmode_driver, \
driver)
/**
* typec_altmode_register_driver - registers a USB Type-C alternate mode
* device driver
* @drv: pointer to struct typec_altmode_driver
*
* These drivers will be bind to the partner alternate mode devices. They will
* handle all SVID specific communication.
*/
#define typec_altmode_register_driver(drv) \
__typec_altmode_register_driver(drv, THIS_MODULE)
int __typec_altmode_register_driver(struct typec_altmode_driver *drv,
struct module *module);
/**
* typec_altmode_unregister_driver - unregisters a USB Type-C alternate mode
* device driver
* @drv: pointer to struct typec_altmode_driver
*
* These drivers will be bind to the partner alternate mode devices. They will
* handle all SVID specific communication.
*/
void typec_altmode_unregister_driver(struct typec_altmode_driver *drv);
#define module_typec_altmode_driver(__typec_altmode_driver) \
module_driver(__typec_altmode_driver, typec_altmode_register_driver, \
typec_altmode_unregister_driver)
#endif /* __USB_TYPEC_ALTMODE_H */