Commit ed4596e9 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

greybus: host: provide "generic" apbridge output calls

Provide a new function, gb_hd_output() to send data to the apbridge.
This is useful for the camera and audio drivers that need to do this
type of messaging.

The camera driver is converted to use this new function, the audio
driver can use it when it gets merged later.
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
Tested-by: default avatarMark Greer <mgreer@animalcreek.com>
Reviewed-by: default avatarJohan Hovold <johan@hovoldconsulting.com>
parent accad1ba
......@@ -16,7 +16,6 @@
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include "es2.h"
#include "greybus.h"
#include "greybus_protocols.h"
......@@ -74,6 +73,17 @@ struct gb_camera_stream_config {
* Camera Protocol Operations
*/
/* vendor request to control the CSI transmitter */
#define REQUEST_CSI_TX_CONTROL 0x08
struct ap_csi_config_request {
__u8 csi_id;
__u8 clock_mode;
__u8 num_lanes;
__u8 padding;
__le32 bus_freq;
} __packed;
static int gb_camera_configure_streams(struct gb_camera *gcam,
unsigned int nstreams,
unsigned int flags,
......@@ -81,7 +91,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
{
struct gb_camera_configure_streams_request *req;
struct gb_camera_configure_streams_response *resp;
struct es2_ap_csi_config csi_cfg;
struct ap_csi_config_request csi_cfg;
unsigned int i;
size_t req_size;
size_t resp_size;
......@@ -150,21 +160,22 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
}
}
memset(&csi_cfg, 0, sizeof(csi_cfg));
/* Configure the CSI transmitter. Hardcode the parameters for now. */
if (nstreams && !(resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED)) {
csi_cfg.csi_id = 1;
csi_cfg.clock_mode = 0;
csi_cfg.num_lanes = 4;
csi_cfg.bus_freq = 960000000;
ret = es2_ap_csi_setup(gcam->connection->hd, true, &csi_cfg);
csi_cfg.bus_freq = cpu_to_le32(960000000);
ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
sizeof(csi_cfg), REQUEST_CSI_TX_CONTROL,
false);
} else if (nstreams == 0) {
csi_cfg.csi_id = 1;
csi_cfg.clock_mode = 0;
csi_cfg.num_lanes = 0;
csi_cfg.bus_freq = 0;
ret = es2_ap_csi_setup(gcam->connection->hd, false, &csi_cfg);
ret = gb_hd_output(gcam->connection->hd, &csi_cfg,
sizeof(csi_cfg), REQUEST_CSI_TX_CONTROL,
false);
}
if (ret < 0)
......
......@@ -13,7 +13,6 @@
#include <linux/debugfs.h>
#include <asm/unaligned.h>
#include "es2.h"
#include "greybus.h"
#include "kernel_ver.h"
#include "connection.h"
......@@ -134,14 +133,6 @@ struct cport_to_ep {
__u8 endpoint_out;
};
struct es2_ap_csi_config_request {
__u8 csi_id;
__u8 clock_mode;
__u8 num_lanes;
__u8 padding;
__le32 bus_freq;
} __packed;
static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd)
{
return (struct es2_ap_dev *)&hd->hd_priv;
......@@ -220,38 +211,87 @@ static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id)
}
#endif
int es2_ap_csi_setup(struct gb_host_device *hd, bool start,
struct es2_ap_csi_config *cfg)
static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
{
struct es2_ap_csi_config_request *cfg_req;
struct es2_ap_dev *es2 = hd_to_es2(hd);
struct usb_device *udev = es2->usb_dev;
u8 *data;
int retval;
cfg_req = kzalloc(sizeof(*cfg_req), GFP_KERNEL);
if (!cfg_req)
data = kmalloc(size, GFP_KERNEL);
if (!data)
return -ENOMEM;
cfg_req->csi_id = cfg->csi_id;
if (start) {
cfg_req->clock_mode = cfg->clock_mode;
cfg_req->num_lanes = cfg->num_lanes;
cfg_req->bus_freq = cpu_to_le32(cfg->bus_freq);
}
memcpy(data, req, size);
retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
REQUEST_CSI_TX_CONTROL,
cmd,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_INTERFACE, 0, 0, cfg_req,
sizeof(*cfg_req), ES2_TIMEOUT);
USB_RECIP_INTERFACE,
0, 0, data, size, ES2_TIMEOUT);
if (retval < 0)
dev_err(&udev->dev, "failed to setup csi: %d\n", retval);
dev_err(&udev->dev, "%s: return error %d\n", __func__, retval);
else
retval = 0;
kfree(cfg_req);
kfree(data);
return retval;
}
EXPORT_SYMBOL_GPL(es2_ap_csi_setup);
static void ap_urb_complete(struct urb *urb)
{
struct usb_ctrlrequest *dr = urb->context;
kfree(dr);
usb_free_urb(urb);
}
static int output_async(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
{
struct usb_device *udev = es2->usb_dev;
struct urb *urb;
struct usb_ctrlrequest *dr;
u8 *buf;
int retval;
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb)
return -ENOMEM;
dr = kmalloc(sizeof(*dr) + size, GFP_ATOMIC);
if (!dr) {
usb_free_urb(urb);
return -ENOMEM;
}
buf = (u8 *)dr + sizeof(*dr);
memcpy(buf, req, size);
dr->bRequest = cmd;
dr->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
dr->wValue = 0;
dr->wIndex = 0;
dr->wLength = cpu_to_le16(size);
usb_fill_control_urb(urb, udev, usb_sndctrlpipe(udev, 0),
(unsigned char *)dr, buf, size,
ap_urb_complete, dr);
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval) {
usb_free_urb(urb);
kfree(dr);
}
return retval;
}
static int output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
bool async)
{
struct es2_ap_dev *es2 = hd_to_es2(hd);
if (async)
return output_async(es2, req, size, cmd);
return output_sync(es2, req, size, cmd);
}
static int es2_cport_in_enable(struct es2_ap_dev *es2,
struct es2_cport_in *cport_in)
......@@ -560,6 +600,7 @@ static struct gb_hd_driver es2_driver = {
.cport_enable = cport_enable,
.latency_tag_enable = latency_tag_enable,
.latency_tag_disable = latency_tag_disable,
.output = output,
};
/* Common function to report consistent warnings based on URB status */
......
/*
* Greybus "AP" USB driver for "ES2" controller chips
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#ifndef __ES2_H
#define __ES2_H
#include <linux/types.h>
struct gb_host_device;
struct es2_ap_csi_config {
u8 csi_id;
u8 clock_mode;
u8 num_lanes;
u32 bus_freq;
};
int es2_ap_csi_setup(struct gb_host_device *hd, bool start,
struct es2_ap_csi_config *cfg);
#endif /* __ES2_H */
......@@ -17,6 +17,15 @@
static struct ida gb_hd_bus_id_map;
int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
bool async)
{
if (!hd || !hd->driver || !hd->driver->output)
return -EINVAL;
return hd->driver->output(hd, req, size, cmd, async);
}
EXPORT_SYMBOL_GPL(gb_hd_output);
static void gb_hd_release(struct device *dev)
{
struct gb_host_device *hd = to_gb_host_device(dev);
......
......@@ -23,6 +23,8 @@ struct gb_hd_driver {
void (*message_cancel)(struct gb_message *message);
int (*latency_tag_enable)(struct gb_host_device *hd, u16 cport_id);
int (*latency_tag_disable)(struct gb_host_device *hd, u16 cport_id);
int (*output)(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
bool async);
};
struct gb_host_device {
......@@ -53,6 +55,8 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver,
int gb_hd_add(struct gb_host_device *hd);
void gb_hd_del(struct gb_host_device *hd);
void gb_hd_put(struct gb_host_device *hd);
int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
bool in_irq);
int gb_hd_init(void);
void gb_hd_exit(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment