* Re: [PATCH 1/2] [media] Add Rockchip RK1608 driver
From: Jacob Chen @ 2017-12-22 4:33 UTC (permalink / raw)
To: Leo Wen
Cc: Mauro Carvalho Chehab, robh+dt, mark.rutland, davem, gregkh,
rdunlap, Linux Media Mailing List, devicetree, linux-kernel,
open list:ARM/Rockchip SoC..., Eddie Cai
In-Reply-To: <1513060095-29588-2-git-send-email-leo.wen@rock-chips.com>
Hi leo,
2017-12-12 14:28 GMT+08:00 Leo Wen <leo.wen@rock-chips.com>:
> Rk1608 is used as a PreISP to link on Soc, which mainly has two functions.
> One is to download the firmware of RK1608, and the other is to match the
> extra sensor such as camera and enable sensor by calling sensor's s_power.
>
> use below v4l2-ctl command to capture frames.
>
> v4l2-ctl --verbose -d /dev/video1 --stream-mmap=2
> --stream-to=/tmp/stream.out --stream-count=60 --stream-poll
>
> use below command to playback the video on your PC.
>
> mplayer ./stream.out -loop 0 -demuxer rawvideo -rawvideo
> w=640:h=480:size=$((640*480*3/2)):format=NV12
>
> Signed-off-by: Leo Wen <leo.wen@rock-chips.com>
> ---
> MAINTAINERS | 6 +
> drivers/media/spi/Makefile | 1 +
> drivers/media/spi/rk1608.c | 1165 ++++++++++++++++++++++++++++++++++++++++++++
> drivers/media/spi/rk1608.h | 366 ++++++++++++++
> 4 files changed, 1538 insertions(+)
> create mode 100644 drivers/media/spi/rk1608.c
> create mode 100644 drivers/media/spi/rk1608.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 82ad0ea..48235d8 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -128,6 +128,12 @@ Maintainers List (try to look for most precise areas first)
>
> -----------------------------------
>
> +ROCKCHIP RK1608 DRIVER
> +M: Leo Wen <leo.wen@rock-chips.com>
> +S: Maintained
> +F: drivers/media/platform/spi/rk1608.c
> +F: drivers/media/platform/spi/rk1608.h
> +
> 3C59X NETWORK DRIVER
> M: Steffen Klassert <klassert@mathematik.tu-chemnitz.de>
> L: netdev@vger.kernel.org
> diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile
> index ea64013..9d9d9ec 100644
> --- a/drivers/media/spi/Makefile
> +++ b/drivers/media/spi/Makefile
> @@ -1 +1,2 @@
> obj-$(CONFIG_VIDEO_GS1662) += gs1662.o
> +obj-$(CONFIG_ROCKCHIP_RK1608) += rk1608.o
> diff --git a/drivers/media/spi/rk1608.c b/drivers/media/spi/rk1608.c
> new file mode 100644
> index 0000000..e646204
> --- /dev/null
> +++ b/drivers/media/spi/rk1608.c
> @@ -0,0 +1,1165 @@
> +/**
> + * Rockchip rk1608 driver
> + *
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + *
> + * This software is available to you under a choice of one of two
> + * licenses. You may choose to be licensed under the terms of the GNU
> + * General Public License (GPL) Version 2, available from the file
> + * COPYING in the main directory of this source tree, or the
> + * OpenIB.org BSD license below:
> + *
> + * Redistribution and use in source and binary forms, with or
> + * without modification, are permitted provided that the following
> + * conditions are met:
> + *
> + * - Redistributions of source code must retain the above
> + * copyright notice, this list of conditions and the following
> + * disclaimer.
> + *
> + * - Redistributions in binary form must reproduce the above
> + * copyright notice, this list of conditions and the following
> + * disclaimer in the documentation and/or other materials
> + * provided with the distribution.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +#include <linux/delay.h>
> +#include <linux/firmware.h>
> +#include <linux/interrupt.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_gpio.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-image-sizes.h>
> +#include <media/v4l2-mediabus.h>
> +#include <media/v4l2-of.h>
> +#include "rk1608.h"
> +
> +/**
> + * Rk1608 is used as the Pre-ISP to link on Soc, which mainly has two
> + * functions. One is to download the firmware of RK1608, and the other
> + * is to match the extra sensor such as camera and enable sensor by
> + * calling sensor's s_power.
> + * |-----------------------|
> + * | Sensor Camera |
> + * |-----------------------|
> + * |-----------||----------|
> + * |-----------||----------|
> + * |-----------\/----------|
> + * | Pre-ISP RK1608 |
> + * |-----------------------|
> + * |-----------||----------|
> + * |-----------||----------|
> + * |-----------\/----------|
> + * | Rockchip Soc |
> + * |-----------------------|
> + * Data Transfer As shown above. In RK1608, the data received from the
> + * extra sensor,and it is passed to the Soc through ISP.
> + */
> +struct rk1608_state {
> + struct v4l2_subdev sd;
> + struct v4l2_subdev *sensor_sd;
> + struct device *dev;
> + struct spi_device *spi;
> + struct media_pad pad;
> + struct clk *mclk;
> + struct mutex lock; /* protect resource */
> + struct mutex sensor_lock; /* protect sensor */
> + struct mutex send_msg_lock; /* protect msg */
> + int power_count;
> + int reset_gpio;
> + int reset_active;
> + int irq_gpio;
> + int irq;
> + int sleepst_gpio;
> + int sleepst_irq;
> + int wakeup_gpio;
> + int wakeup_active;
> + int powerdown_gpio;
> + int powerdown_active;
> + int msg_num;
> + u32 sensor_cnt;
> + u32 sensor_nums;
> + u32 max_speed_hz;
> + u32 min_speed_hz;
> + atomic_t msg_done[8];
> + wait_queue_head_t msg_wait;
> + struct v4l2_ctrl *link_freq;
> + struct v4l2_ctrl_handler ctrl_handler;
> +};
> +
> +static const s64 link_freq_menu_items[] = {
> + 1000000000
> +};
> +
> +static inline struct rk1608_state *to_state(struct v4l2_subdev *sd)
> +{
> + return container_of(sd, struct rk1608_state, sd);
> +}
> +
> +/**
> + * rk1608_operation_query - RK1608 last operation state query
> + *
> + * @spi: device from which data will be read
> + * @state: last operation state [out]
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_operation_query(struct spi_device *spi, s32 *state)
> +{
> + s32 query_cmd = RK1608_CMD_QUERY;
> + struct spi_transfer query_cmd_packet = {
> + .tx_buf = &query_cmd,
> + .len = sizeof(query_cmd),
> + };
> + struct spi_transfer state_packet = {
> + .rx_buf = state,
> + .len = sizeof(*state),
> + };
> + struct spi_message m;
> +
> + spi_message_init(&m);
> + spi_message_add_tail(&query_cmd_packet, &m);
> + spi_message_add_tail(&state_packet, &m);
> + spi_sync(spi, &m);
> +
> + return ((*state & RK1608_STATE_ID_MASK) == RK1608_STATE_ID) ? 0 : -1;
> +}
> +
> +int rk1608_write(struct spi_device *spi,
> + s32 addr, const s32 *data, size_t data_len)
> +{
> + s32 write_cmd = RK1608_CMD_WRITE;
> + struct spi_transfer write_cmd_packet = {
> + .tx_buf = &write_cmd,
> + .len = sizeof(write_cmd),
> + };
> + struct spi_transfer addr_packet = {
> + .tx_buf = &addr,
> + .len = sizeof(addr),
> + };
> + struct spi_transfer data_packet = {
> + .tx_buf = data,
> + .len = data_len,
> + };
> + struct spi_message m;
> +
> + spi_message_init(&m);
> + spi_message_add_tail(&write_cmd_packet, &m);
> + spi_message_add_tail(&addr_packet, &m);
> + spi_message_add_tail(&data_packet, &m);
> + return spi_sync(spi, &m);
> +}
> +
> +/**
> + * rk1608_safe_write - RK1608 synchronous write with state check
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else operation state code.
> + */
> +int rk1608_safe_write(struct spi_device *spi,
> + s32 addr, const s32 *data, size_t data_len)
> +{
> + int ret = 0;
> + s32 state, retry = 0;
> +
> + while (data_len > 0) {
> + size_t slen = MIN(data_len, RK1608_MAX_OP_BYTES);
> +
> + do {
> + rk1608_write(spi, addr, data, data_len);
> + if (rk1608_operation_query(spi, &state) != 0)
> + return -1;
> + if ((state & RK1608_STATE_MASK) == 0)
> + break;
> +
> + udelay(RK1608_OP_TRY_DELAY);
> + } while (retry++ != RK1608_OP_TRY_MAX);
> +
> + data_len = data_len - slen;
> + data = (s32 *)((s8 *)data + slen);
> + addr += slen;
> + }
> + return ret;
> +}
> +
> +void rk1608_hw_init(struct spi_device *spi)
> +{
> + s32 write_data = SPI0_PLL_SEL_APLL;
> +
> + /* modify rk1608 spi slave clk to 300M */
> + rk1608_safe_write(spi, CRUPMU_CLKSEL14_CON, &write_data, 4);
> +
> + /* modify rk1608 spi io driver strength to 8mA */
> + write_data = BIT7_6_SEL_8MA;
> + rk1608_safe_write(spi, PMUGRF_GPIO1A_E, &write_data, 4);
> + write_data = BIT1_0_SEL_8MA;
> + rk1608_safe_write(spi, PMUGRF_GPIO1B_E, &write_data, 4);
> +}
> +
> +/**
> + * rk1608_read - RK1608 synchronous read
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer [out]
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_read(struct spi_device *spi,
> + s32 addr, s32 *data, size_t data_len)
> +{
> + s32 real_len = MIN(data_len, RK1608_MAX_OP_BYTES);
> + s32 read_cmd = RK1608_CMD_READ | (real_len << 14 &
> + RK1608_STATE_ID_MASK);
> + s32 read_begin_cmd = RK1608_CMD_READ_BEGIN;
> + s32 dummy = 0;
> + struct spi_transfer read_cmd_packet = {
> + .tx_buf = &read_cmd,
> + .len = sizeof(read_cmd),
> + };
> + struct spi_transfer addr_packet = {
> + .tx_buf = &addr,
> + .len = sizeof(addr),
> + };
> + struct spi_transfer read_dummy_packet = {
> + .tx_buf = &dummy,
> + .len = sizeof(dummy),
> + };
> + struct spi_transfer read_begin_cmd_packet = {
> + .tx_buf = &read_begin_cmd,
> + .len = sizeof(read_begin_cmd),
> + };
> + struct spi_transfer data_packet = {
> + .rx_buf = data,
> + .len = data_len,
> + };
> + struct spi_message m;
> +
> + spi_message_init(&m);
> + spi_message_add_tail(&read_cmd_packet, &m);
> + spi_message_add_tail(&addr_packet, &m);
> + spi_message_add_tail(&read_dummy_packet, &m);
> + spi_message_add_tail(&read_begin_cmd_packet, &m);
> + spi_message_add_tail(&data_packet, &m);
> + return spi_sync(spi, &m);
> +}
> +
> +/**
> + * rk1608_safe_read - RK1608 synchronous read with state check
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer [out]
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else operation state code.
> + */
> +int rk1608_safe_read(struct spi_device *spi,
> + s32 addr, s32 *data, size_t data_len)
> +{
> + s32 state = 0;
> + s32 retry = 0;
> +
> + do {
> + rk1608_read(spi, addr, data, data_len);
> + if (rk1608_operation_query(spi, &state) != 0)
> + return -1;
> + if ((state & RK1608_STATE_MASK) == 0)
> + break;
> + udelay(RK1608_OP_TRY_DELAY);
> + } while (retry++ != RK1608_OP_TRY_MAX);
> +
> + return (state & RK1608_STATE_MASK);
> +}
> +
> +static int rk1608_read_wait(struct spi_device *spi,
> + const struct rk1608_section *sec)
> +{
> + s32 value = 0;
> + int retry = 0;
> + int ret = 0;
> +
> + do {
> + ret = rk1608_safe_read(spi, sec->wait_addr, &value, 4);
> + if (!ret && value == sec->wait_value)
> + break;
> +
> + if (retry++ == sec->timeout) {
> + ret = -1;
> + dev_err(&spi->dev, "read 0x%x is %x != %x timeout\n",
> + sec->wait_addr, value, sec->wait_value);
> + break;
> + }
> + mdelay(sec->wait_time);
> + } while (1);
> +
> + return ret;
> +}
> +
> +static int rk1608_boot_request(struct spi_device *spi,
> + const struct rk1608_section *sec)
> +{
> + struct rk1608_boot_req boot_req;
> + int retry = 0;
> + int ret = 0;
> +
> + /*send boot request to rk1608 for ddr init*/
> + boot_req.flag = sec->flag;
> + boot_req.load_addr = sec->load_addr;
> + boot_req.boot_len = sec->size;
> + boot_req.status = 1;
> + boot_req.cmd = 2;
> +
> + ret = rk1608_safe_write(spi, BOOT_REQUEST_ADDR,
> + (s32 *)&boot_req, sizeof(boot_req));
> + if (ret)
> + return ret;
> +
> + if (sec->flag & BOOT_FLAG_READ_WAIT) {
> + /*waitting for rk1608 init ddr done*/
> + do {
> + ret = rk1608_safe_read(spi, BOOT_REQUEST_ADDR,
> + (s32 *)&boot_req,
> + sizeof(boot_req));
> +
> + if (!ret && boot_req.status == 0)
> + break;
> +
> + if (retry++ == sec->timeout) {
> + ret = -1;
> + dev_err(&spi->dev, "boot_request timeout\n");
> + break;
> + }
> + mdelay(sec->wait_time);
> + } while (1);
> + }
> +
> + return ret;
> +}
> +
> +static int rk1608_download_section(struct spi_device *spi, const u8 *data,
> + const struct rk1608_section *sec)
> +{
> + int ret = 0;
> +
> + dev_info(&spi->dev, "offset:%x,size:%x,addr:%x,wait_time:%x",
> + sec->offset, sec->size, sec->load_addr, sec->wait_time);
> + dev_info(&spi->dev, "timeout:%x,crc:%x,flag:%x,type:%x",
> + sec->timeout, sec->crc_16, sec->flag, sec->type);
> +
> + if (sec->size > 0) {
> + ret = rk1608_safe_write(spi, sec->load_addr,
> + (s32 *)(data + sec->offset),
> + sec->size);
> + if (ret) {
> + dev_err(&spi->dev, "rk1608_safe_write err =%d\n", ret);
> + return ret;
> + }
> + }
> +
> + if (sec->flag & BOOT_FLAG_BOOT_REQUEST)
> + ret = rk1608_boot_request(spi, sec);
> + else if (sec->flag & BOOT_FLAG_READ_WAIT)
> + ret = rk1608_read_wait(spi, sec);
> +
> + return ret;
> +}
> +
> +/**
> + * rk1608_download_fw: - rk1608 firmware download through spi
> + *
> + * @spi: spi device
> + * @fw_name: name of firmware file, NULL for default firmware name
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + **/
> +int rk1608_download_fw(struct spi_device *spi, const char *fw_name)
> +{
> + const struct rk1608_header *head;
> + const struct firmware *fw;
> + int i = 0;
> + int ret = 0;
> +
> + if (!fw_name)
> + fw_name = RK1608_FW_NAME;
> +
> + dev_info(&spi->dev, "before request firmware");
> + ret = request_firmware(&fw, fw_name, &spi->dev);
> + if (ret) {
> + dev_err(&spi->dev, "request firmware %s failed!", fw_name);
> + return ret;
> + }
> +
> + head = (const struct rk1608_header *)fw->data;
> +
> + dev_info(&spi->dev, "request firmware %s (version:%s) success!",
> + fw_name, head->version);
> +
> + for (i = 0; i < head->section_count; i++) {
> + ret = rk1608_download_section(spi, fw->data,
> + &head->sections[i]);
> + if (ret)
> + break;
> + }
> +
> + release_firmware(fw);
> + return ret;
> +}
> +
> +int rk1608_lsb_w32(struct spi_device *spi, s32 addr, s32 data)
> +{
> + s32 write_cmd = RK1608_CMD_WRITE;
> + struct spi_transfer write_cmd_packet = {
> + .tx_buf = &write_cmd,
> + .len = sizeof(write_cmd),
> + };
> + struct spi_transfer addr_packet = {
> + .tx_buf = &addr,
> + .len = sizeof(addr),
> + };
> + struct spi_transfer data_packet = {
> + .tx_buf = &data,
> + .len = sizeof(data),
> + };
> + struct spi_message m;
> +
> + write_cmd = MSB2LSB32(write_cmd);
> + addr = MSB2LSB32(addr);
> + data = MSB2LSB32(data);
> + spi_message_init(&m);
> + spi_message_add_tail(&write_cmd_packet, &m);
> + spi_message_add_tail(&addr_packet, &m);
> + spi_message_add_tail(&data_packet, &m);
> + return spi_sync(spi, &m);
> +}
> +
> +void rk1608_cs_set_value(struct rk1608_state *pdata, int value)
> +{
> + s8 null_cmd = 0;
> + struct spi_transfer null_cmd_packet = {
> + .tx_buf = &null_cmd,
> + .len = sizeof(null_cmd),
> + .cs_change = !value,
> + };
> + struct spi_message m;
> +
> + spi_message_init(&m);
> + spi_message_add_tail(&null_cmd_packet, &m);
> + spi_sync(pdata->spi, &m);
> +}
> +
> +void rk1608_set_spi_speed(struct rk1608_state *pdata, u32 hz)
> +{
> + pdata->spi->max_speed_hz = hz;
> +}
> +
> +static int rk1608_sensor_power(struct v4l2_subdev *sd, int on)
> +{
> + int ret = 0;
> + struct rk1608_state *pdata = to_state(sd);
> + struct spi_device *spi = pdata->spi;
> +
> + mutex_lock(&pdata->lock);
> + /*start Sensor power on/off*/
> + if (pdata->sensor_sd)
> + pdata->sensor_sd->ops->core->s_power(pdata->sensor_sd, on);
> + if (on && !pdata->power_count) {
> + clk_prepare_enable(pdata->mclk);
> + clk_set_rate(pdata->mclk, RK1608_MCLK_RATE);
> + /*request rk1608 enter slave mode*/
> + rk1608_cs_set_value(pdata, 0);
> + if (pdata->powerdown_gpio > 0) {
> + gpio_set_value(pdata->powerdown_gpio,
> + pdata->powerdown_active);
> + }
> + if (pdata->wakeup_gpio > 0) {
> + gpio_set_value(pdata->wakeup_gpio,
> + pdata->wakeup_active);
> + }
> + mdelay(3);
> + if (pdata->reset_gpio > 0)
> + gpio_set_value(pdata->reset_gpio, pdata->reset_active);
> + mdelay(5);
> + rk1608_cs_set_value(pdata, 1);
> + rk1608_set_spi_speed(pdata, pdata->min_speed_hz);
> + rk1608_lsb_w32(spi, SPI_ENR, 0);
> + rk1608_lsb_w32(spi, SPI_CTRL0,
> + OPM_SLAVE_MODE | RSD_SEL_2CYC | DFS_SEL_16BIT);
> + rk1608_hw_init(pdata->spi);
> + rk1608_set_spi_speed(pdata, pdata->max_speed_hz);
> + /*download system firmware*/
> + ret = rk1608_download_fw(pdata->spi, NULL);
> + if (ret)
> + dev_err(pdata->dev, "Download firmware failed!");
> + else
> + dev_info(pdata->dev, "Download firmware success!");
> + enable_irq(pdata->irq);
> + if (pdata->sleepst_irq > 0)
> + enable_irq(pdata->sleepst_irq);
> +
> + } else if (!on && pdata->power_count == 1) {
> + disable_irq(pdata->irq);
> + if (pdata->sleepst_irq > 0)
> + disable_irq(pdata->sleepst_irq);
> + if (pdata->powerdown_gpio > 0)
> + gpio_set_value(pdata->powerdown_gpio,
> + !pdata->powerdown_active);
> +
> + if (pdata->wakeup_gpio > 0)
> + gpio_set_value(pdata->wakeup_gpio,
> + !pdata->wakeup_active);
> +
> + if (pdata->reset_gpio > 0)
> + gpio_set_value(pdata->reset_gpio, !pdata->reset_active);
> +
> + rk1608_cs_set_value(pdata, 0);
> + clk_disable_unprepare(pdata->mclk);
> + }
> + /* Update the power count. */
> + pdata->power_count += on ? 1 : -1;
> + WARN_ON(pdata->power_count < 0);
> + mutex_unlock(&pdata->lock);
> +
> + return ret;
> +}
> +
> +static int rk1608_stream_on(struct rk1608_state *pdata)
> +{
> + int cnt = 0;
> +
> + /*Waiting for the sensor to be ready*/
> + while (pdata->sensor_cnt < pdata->sensor_nums) {
> + /* TIMEOUT 10s break*/
> + if (cnt++ > SENSOR_TIMEOUT) {
> + dev_err(pdata->dev, "Sensor%d is ready to timeout!",
> + pdata->sensor_cnt);
> + break;
> + }
> + mdelay(10);
> + }
> +
> + if (pdata->sensor_nums) {
> + if (pdata->sensor_cnt == pdata->sensor_nums)
> + dev_info(pdata->dev, "Sensor(num %d) is ready!",
> + pdata->sensor_cnt);
> + } else {
> + dev_warn(pdata->dev, "No sensor is found!");
> + }
> +
> + return 0;
> +}
> +
> +static int rk1608_stream_off(struct rk1608_state *pdata)
> +{
> + mutex_lock(&pdata->sensor_lock);
> + pdata->sensor_cnt = 0;
> + mutex_unlock(&pdata->sensor_lock);
> + return 0;
> +}
> +
> +static int rk1608_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + struct rk1608_state *pdata = to_state(sd);
> +
> + if (enable)
> + return rk1608_stream_on(pdata);
> + else
> + return rk1608_stream_off(pdata);
> +}
> +
> +static int rk1608_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + if (code->index > 0)
> + return -EINVAL;
> +
> + code->code = MEDIA_BUS_FMT_SGRBG8_1X8;
> +
> + return 0;
> +}
> +
> +static int rk1608_get_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct v4l2_mbus_framefmt *mf = &fmt->format;
> +
> + mf->code = MEDIA_BUS_FMT_SGRBG8_1X8;
> + mf->width = RK1608_WINDOW_WIDTH_DEF;
> + mf->height = RK1608_WINDOW_HEIGHT_DEF;
> + mf->field = V4L2_FIELD_NONE;
> + mf->colorspace = V4L2_COLORSPACE_SRGB;
> +
> + return 0;
> +}
> +
> +static const struct v4l2_subdev_internal_ops rk1608_subdev_internal_ops = {
> + .open = NULL,
> +};
> +
> +static const struct v4l2_subdev_video_ops rk1608_subdev_video_ops = {
> + .s_stream = rk1608_s_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops rk1608_subdev_pad_ops = {
> + .enum_mbus_code = rk1608_enum_mbus_code,
> + .get_fmt = rk1608_get_fmt,
> +};
> +
> +static const struct v4l2_subdev_core_ops rk1608_core_ops = {
> + .s_power = rk1608_sensor_power,
> +};
> +
> +static const struct v4l2_subdev_ops rk1608_subdev_ops = {
> + .core = &rk1608_core_ops,
> + .video = &rk1608_subdev_video_ops,
> + .pad = &rk1608_subdev_pad_ops,
> +};
> +
> +/**
> + * rk1608_msq_read_head - read rk1608 msg queue head
> + *
> + * @spi: spi device
> + * @addr: msg queue head addr
> + * @m: msg queue pointer
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_msq_read_head(struct spi_device *spi,
> + u32 addr, struct rk1608_msg_queue *q)
> +{
> + int err = 0;
> + s32 reg;
> +
> + err = rk1608_safe_read(spi, RK1608_PMU_SYS_REG0, ®, 4);
> +
> + if (err || ((reg & RK1608_MSG_QUEUE_OK_MASK) !=
> + RK1608_MSG_QUEUE_OK_TAG))
> + return -1;
> +
> + err = rk1608_safe_read(spi, addr, (s32 *)q, sizeof(*q));
> +
> + return err;
> +}
> +
> +/**
> + * rk1608_msq_recv_msg - receive a msg from RK1608 -> AP msg queue
> + *
> + * @q: msg queue
> + * @m: a msg pointer buf [out]
> + *
> + * need call rk1608_msq_recv_msg_free to free msg after msg use done
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_msq_recv_msg(struct spi_device *spi, struct msg **m)
> +{
> + struct rk1608_msg_queue queue;
> + struct rk1608_msg_queue *q = &queue;
> + u32 size = 0, msg_size = 0;
> + u32 recv_addr = 0;
> + u32 next_recv_addr = 0;
> + int err = 0;
> +
> + *m = NULL;
> + err = rk1608_msq_read_head(spi, RK1608_S_MSG_QUEUE_ADDR, q);
> + if (err)
> + return err;
> +
> + if (q->cur_send == q->cur_recv)
> + return -1;
> + /*skip to head when size is 0*/
> + err = rk1608_safe_read(spi, (s32)q->cur_recv, (s32 *)&size, 4);
> + if (err)
> + return err;
> + if (size == 0) {
> + err = rk1608_safe_read(spi, (s32)q->buf_head, (s32 *)&size, 4);
> + if (err)
> + return err;
> +
> + msg_size = size * sizeof(u32);
> + recv_addr = q->buf_head;
> + next_recv_addr = q->buf_head + msg_size;
> + } else {
> + msg_size = size * sizeof(u32);
> + recv_addr = q->cur_recv;
> + next_recv_addr = q->cur_recv + msg_size;
> + if (next_recv_addr == q->buf_tail)
> + next_recv_addr = q->buf_head;
> + }
> +
> + if (msg_size > (q->buf_tail - q->buf_head))
> + return -2;
> +
> + *m = kmalloc(msg_size, GFP_KERNEL);
> + err = rk1608_safe_read(spi, recv_addr, (s32 *)*m, msg_size);
> + if (err == 0) {
> + err = rk1608_safe_write(spi, RK1608_S_MSG_QUEUE_ADDR +
> + (u8 *)&q->cur_recv - (u8 *)q,
> + &next_recv_addr, 4);
> + }
> + if (err)
> + kfree(*m);
> +
> + return err;
> +}
> +
> +static void print_rk1608_log(struct rk1608_state *pdata,
> + struct msg_rk1608_log_t *log)
> +{
> + char *str = (char *)(log);
> +
> + str[log->size * sizeof(s32) - 1] = 0;
> + str += sizeof(struct msg_rk1608_log_t);
> + dev_info(pdata->dev, "RK1608%d: %s", log->core_id, str);
> +}
> +
> +void int32_hexdump(const char *prefix, int32_t *data, int len)
> +{
> + pr_err("%s\n", prefix);
> + print_hex_dump(KERN_ERR, "offset ", DUMP_PREFIX_OFFSET,
> + 16, 4, data, len, false);
> + pr_err("\n");
> +}
> +
> +static void dispatch_received_msg(struct rk1608_state *pdata,
> + struct msg *msg)
> +{
> + #if DEBUG_DUMP_ALL_SEND_RECV_MSG == 1
> + int32_hexdump("recv msg:", (s32 *)msg, msg->size * 4);
> + #endif
> +
> + if (msg->type == id_msg_set_stream_out_on_ret_t) {
> + mutex_lock(&pdata->sensor_lock);
> + pdata->sensor_cnt++;
> + mutex_unlock(&pdata->sensor_lock);
> + }
> +
> + if (msg->type == id_msg_rk1608_log_t)
> + print_rk1608_log(pdata, (struct msg_rk1608_log_t *)msg);
> +}
> +
> +static irqreturn_t rk1608_threaded_isr(int irq, void *dev_id)
> +{
> + struct rk1608_state *pdata = dev_id;
> + struct msg *msg;
> +
> + WARN_ON(irq != pdata->irq);
> + while (!rk1608_msq_recv_msg(pdata->spi, &msg) && NULL != msg) {
> + dispatch_received_msg(pdata, msg);
> + /* for kernel msg sync */
> + if (pdata->msg_num != 0 && msg->sync) {
> + dev_info(pdata->dev, "rk1608 kernel sync\n");
> + mutex_lock(&pdata->send_msg_lock);
> + pdata->msg_num--;
> + atomic_set(&pdata->msg_done[pdata->msg_num], 1);
> + mutex_unlock(&pdata->send_msg_lock);
> + wake_up(&pdata->msg_wait);
> + }
> + kfree(msg);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t rk1608_sleep_isr(int irq, void *dev_id)
> +{
> + struct rk1608_state *pdata = dev_id;
> +
> + WARN_ON(irq != pdata->sleepst_irq);
> + if (pdata->powerdown_gpio > 0)
> + gpio_set_value(pdata->powerdown_gpio, !pdata->powerdown_active);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int rk1608_parse_dt_property(struct rk1608_state *pdata)
> +{
> + int ret = 0;
> + int i;
> + struct device *dev = pdata->dev;
> + struct device_node *node = dev->of_node;
> + enum of_gpio_flags flags;
> +
> + if (!node)
> + return 1;
> +
> + ret = of_property_read_u32(node, "spi-max-frequency",
> + &pdata->max_speed_hz);
> + if (ret <= 0) {
> + dev_warn(dev, "can not get spi-max-frequency!");
> + pdata->max_speed_hz = RK1608_MCLK_RATE;
> + }
> +
> + ret = of_property_read_u32(node, "spi-min-frequency",
> + &pdata->min_speed_hz);
> + if (ret <= 0) {
> + dev_warn(dev, "can not get spi-min-frequency!");
> + pdata->min_speed_hz = pdata->max_speed_hz / 2;
> + }
> +
> + pdata->mclk = devm_clk_get(dev, "mclk");
> + if (IS_ERR(pdata->mclk)) {
> + dev_err(dev, "can not get mclk, error %ld\n",
> + PTR_ERR(pdata->mclk));
> + pdata->mclk = NULL;
> + return -1;
> + }
> +
> + ret = of_get_named_gpio_flags(node, "reset-gpio", 0, &flags);
> + if (ret <= 0) {
> + dev_warn(dev, "can not find reset-gpio, error %d\n", ret);
> + return ret;
> + }
> + pdata->reset_gpio = ret;
> + pdata->reset_active = 1;
> + if (flags == OF_GPIO_ACTIVE_LOW)
> + pdata->reset_active = 0;
> +
> + if (pdata->reset_gpio > 0) {
> + ret = devm_gpio_request(dev, pdata->reset_gpio, "rk1608-reset");
> + if (ret) {
> + dev_err(dev, "gpio %d request error %d\n",
> + pdata->reset_gpio, ret);
> + return ret;
> + }
> +
> + ret = gpio_direction_output(pdata->reset_gpio,
> + !pdata->reset_active);
> + if (ret) {
> + dev_err(dev, "gpio %d direction output error %d\n",
> + pdata->reset_gpio, ret);
> + return ret;
> + }
> + }
> +
> + ret = of_get_named_gpio_flags(node, "irq-gpio", 0, NULL);
> + if (ret <= 0) {
> + dev_warn(dev, "can not find irq-gpio, error %d\n", ret);
> + return ret;
> + }
> +
> + pdata->irq_gpio = ret;
> +
> + ret = devm_gpio_request(dev, pdata->irq_gpio, "rk1608-irq");
> + if (ret) {
> + dev_err(dev, "gpio %d request error %d\n", pdata->irq_gpio,
> + ret);
> + return ret;
> + }
> +
> + ret = gpio_direction_input(pdata->irq_gpio);
> + if (ret) {
> + dev_err(dev, "gpio %d direction input error %d\n",
> + pdata->irq_gpio, ret);
> + return ret;
> + }
> +
> + ret = gpio_to_irq(pdata->irq_gpio);
> + if (ret < 0) {
> + dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
> + pdata->irq_gpio, ret);
> + return ret;
> + }
> + pdata->irq = ret;
> + ret = request_threaded_irq(pdata->irq, NULL, rk1608_threaded_isr,
> + IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + "rk1608-irq", pdata);
> + if (ret) {
> + dev_err(dev, "cannot request thread irq: %d\n", ret);
> + return ret;
> + }
> +
> + disable_irq(pdata->irq);
> +
> + ret = of_get_named_gpio_flags(node, "powerdown-gpio", 0, &flags);
> + if (ret <= 0)
> + dev_warn(dev, "can not find powerdown-gpio, error %d\n", ret);
> +
> + pdata->powerdown_gpio = ret;
> + pdata->powerdown_active = 1;
> + if (flags == OF_GPIO_ACTIVE_LOW)
> + pdata->powerdown_active = 0;
> +
> + if (pdata->powerdown_gpio > 0) {
> + ret = devm_gpio_request(dev, pdata->powerdown_gpio,
> + "rk1608-powerdown");
> + if (ret) {
> + dev_err(dev, "gpio %d request error %d\n",
> + pdata->powerdown_gpio, ret);
> + return ret;
> + }
> +
> + ret = gpio_direction_output(pdata->powerdown_gpio,
> + !pdata->powerdown_active);
> + if (ret) {
> + dev_err(dev, "gpio %d direction output error %d\n",
> + pdata->powerdown_gpio, ret);
> + return ret;
> + }
> + }
> +
> + pdata->sleepst_gpio = -1;
> + pdata->sleepst_irq = -1;
> + pdata->wakeup_gpio = -1;
> +
> + ret = of_get_named_gpio_flags(node, "sleepst-gpio", 0, NULL);
> + if (ret <= 0) {
> + dev_warn(dev, "can not find property sleepst-gpio, error %d\n",
> + ret);
> + return ret;
> + }
> +
> + pdata->sleepst_gpio = ret;
> +
> + ret = devm_gpio_request(dev, pdata->sleepst_gpio, "rk1608-sleep-irq");
> + if (ret) {
> + dev_err(dev, "gpio %d request error %d\n",
> + pdata->sleepst_gpio, ret);
> + return ret;
> + }
> +
> + ret = gpio_direction_input(pdata->sleepst_gpio);
> + if (ret) {
> + dev_err(dev, "gpio %d direction input error %d\n",
> + pdata->sleepst_gpio, ret);
> + return ret;
> + }
> +
> + ret = gpio_to_irq(pdata->sleepst_gpio);
> + if (ret < 0) {
> + dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
> + pdata->sleepst_gpio, ret);
> + return ret;
> + }
> + pdata->sleepst_irq = ret;
> + ret = request_any_context_irq(pdata->sleepst_irq,
> + rk1608_sleep_isr,
> + IRQF_TRIGGER_RISING,
> + "rk1608-sleepst", pdata);
> + disable_irq(pdata->sleepst_irq);
> +
> + ret = of_get_named_gpio_flags(node, "wakeup-gpio", 0, &flags);
> + if (ret <= 0)
> + dev_warn(dev, "can not find wakeup-gpio error %d\n", ret);
> +
> + pdata->wakeup_gpio = ret;
> + pdata->wakeup_active = 1;
> + if (flags == OF_GPIO_ACTIVE_LOW)
> + pdata->wakeup_active = 0;
> +
> + if (pdata->wakeup_gpio > 0) {
> + ret = devm_gpio_request(dev, pdata->wakeup_gpio,
> + "rk1608-wakeup");
> + if (ret) {
> + dev_err(dev, "gpio %d request error %d\n",
> + pdata->wakeup_gpio, ret);
> + return ret;
> + }
> +
> + ret = gpio_direction_output(pdata->wakeup_gpio,
> + !pdata->wakeup_active);
> + if (ret) {
> + dev_err(dev, "gpio %d direction output error %d\n",
> + pdata->wakeup_gpio, ret);
> + return ret;
> + }
> + }
> + pdata->msg_num = 0;
> + init_waitqueue_head(&pdata->msg_wait);
> + for (i = 0; i < 8; i++)
> + atomic_set(&pdata->msg_done[i], 0);
> +
> + return ret;
> +}
> +
> +static int get_remote_node_dev(struct rk1608_state *pdev)
> +{
> + struct platform_device *sensor_pdev = NULL;
> + struct device *dev = pdev->dev;
> + struct device_node *parent = dev->of_node;
> + struct device_node *node, *pre_node = NULL;
> + struct device_node *remote = NULL;
> + int ret, sensor_nums = 0;
> +
> + node = of_graph_get_next_endpoint(parent, pre_node);
> + if (node) {
> + of_node_put(pre_node);
> + pre_node = node;
> + } else {
> + dev_err(dev, "fieled to get endpoint\n");
> + return -EINVAL;
> + }
> + while ((node = of_graph_get_next_endpoint(parent, pre_node)) != NULL) {
> + of_node_put(pre_node);
> + pre_node = node;
> + remote = of_graph_get_remote_port_parent(node);
> + if (!remote) {
> + dev_err(dev, "%s: no valid device\n", __func__);
> + of_node_put(remote);
> + ret = -EINVAL;
> + }
> +
> + sensor_pdev = of_find_device_by_node(remote);
> + of_node_put(remote);
> +
> + if (!sensor_pdev) {
> + dev_err(dev, "fieled to get Sensor device\n");
> + ret = -EINVAL;
> + } else {
> + pdev->sensor_sd = platform_get_drvdata(sensor_pdev);
> + if (pdev->sensor_sd)
> + sensor_nums++;
> + else
> + dev_err(dev, "fieled to get Sensor drvdata\n");
> + ret = 0;
> + }
> + }
> + pdev->sensor_nums = sensor_nums;
> + if (pdev->sensor_nums)
> + dev_info(dev, "get Sensor (nums=%d) dev is OK!\n",
> + pdev->sensor_nums);
> +
> + return ret;
> +}
> +
> +static int rk1608_probe(struct spi_device *spi)
> +{
> + struct rk1608_state *rk1608;
> + struct v4l2_subdev *sd;
> + struct v4l2_ctrl_handler *handler;
> + int ret;
> +
> + rk1608 = devm_kzalloc(&spi->dev, sizeof(*rk1608), GFP_KERNEL);
> + if (!rk1608)
> + return -ENOMEM;
> + rk1608->dev = &spi->dev;
> + rk1608->spi = spi;
> + spi_set_drvdata(spi, rk1608);
> + ret = rk1608_parse_dt_property(rk1608);
> + if (ret) {
> + dev_err(rk1608->dev, "rk1608 parse dt property err(%x)\n", ret);
> + goto parse_err;
> + }
> + ret = get_remote_node_dev(rk1608);
> + if (ret)
> + dev_warn(rk1608->dev, "get remote node dev err(%x)\n", ret);
> + rk1608->sensor_cnt = 0;
> + mutex_init(&rk1608->sensor_lock);
> + mutex_init(&rk1608->send_msg_lock);
> + mutex_init(&rk1608->lock);
> + sd = &rk1608->sd;
> + v4l2_spi_subdev_init(sd, spi, &rk1608_subdev_ops);
> +
> + handler = &rk1608->ctrl_handler;
> + ret = v4l2_ctrl_handler_init(handler, 1);
> + if (ret)
> + goto handler_init_err;
> +
> + rk1608->link_freq = v4l2_ctrl_new_int_menu(handler, NULL,
> + V4L2_CID_LINK_FREQ,
> + 0, 0, link_freq_menu_items);
> + if (rk1608->link_freq)
> + rk1608->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> + if (handler->error)
> + goto handler_err;
> +
> + sd->ctrl_handler = handler;
> + sd->internal_ops = &rk1608_subdev_internal_ops;
> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> + rk1608->pad.flags = MEDIA_PAD_FL_SOURCE;
> + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
> +
> + ret = media_entity_init(&sd->entity, 1, &rk1608->pad, 0);
> + if (ret < 0)
> + goto handler_err;
> +
> + ret = v4l2_async_register_subdev(sd);
> + if (ret < 0)
> + goto register_err;
> + dev_info(rk1608->dev, "DSP rk1608 Driver probe is OK!\n");
> +
> + return 0;
> +register_err:
> + media_entity_cleanup(&sd->entity);
> +handler_err:
> + v4l2_ctrl_handler_free(handler);
> +handler_init_err:
> + v4l2_device_unregister_subdev(&rk1608->sd);
> + mutex_destroy(&rk1608->lock);
> + mutex_destroy(&rk1608->send_msg_lock);
> + mutex_destroy(&rk1608->sensor_lock);
> +parse_err:
> + kfree(rk1608);
> + return ret;
> +}
> +
> +static int rk1608_remove(struct spi_device *spi)
> +{
> + struct rk1608_state *rk1608 = spi_get_drvdata(spi);
> +
> + v4l2_async_unregister_subdev(&rk1608->sd);
> + media_entity_cleanup(&rk1608->sd.entity);
> + v4l2_ctrl_handler_free(&rk1608->ctrl_handler);
> + v4l2_device_unregister_subdev(&rk1608->sd);
> + mutex_destroy(&rk1608->lock);
> + mutex_destroy(&rk1608->send_msg_lock);
> + mutex_destroy(&rk1608->sensor_lock);
> + kfree(rk1608);
> +
> + return 0;
> +}
> +
> +static const struct spi_device_id rk1608_id[] = {
> + { "RK1608", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(spi, rk1608_id);
> +
> +#if IS_ENABLED(CONFIG_OF)
> +static const struct of_device_id rk1608_of_match[] = {
> + { .compatible = "rockchip,rk1608" },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, rk1608_of_match);
> +#endif
> +
> +static struct spi_driver rk1608_driver = {
> + .driver = {
> + .of_match_table = of_match_ptr(rk1608_of_match),
> + .name = "RK1608",
> + },
> + .probe = rk1608_probe,
> + .remove = rk1608_remove,
> + .id_table = rk1608_id,
> +};
> +
> +module_spi_driver(rk1608_driver);
> +
> +MODULE_AUTHOR("Rockchip Camera/ISP team");
> +MODULE_DESCRIPTION("A DSP driver for rk1608 chip");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/media/spi/rk1608.h b/drivers/media/spi/rk1608.h
> new file mode 100644
> index 0000000..bf0c5ec
> --- /dev/null
> +++ b/drivers/media/spi/rk1608.h
> @@ -0,0 +1,366 @@
> +/**
> + * Rockchip rk1608 driver
> + *
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + *
> + * This software is available to you under a choice of one of two
> + * licenses. You may choose to be licensed under the terms of the GNU
> + * General Public License (GPL) Version 2, available from the file
> + * COPYING in the main directory of this source tree, or the
> + * OpenIB.org BSD license below:
> + *
> + * Redistribution and use in source and binary forms, with or
> + * without modification, are permitted provided that the following
> + * conditions are met:
> + *
> + * - Redistributions of source code must retain the above
> + * copyright notice, this list of conditions and the following
> + * disclaimer.
> + *
> + * - Redistributions in binary form must reproduce the above
> + * copyright notice, this list of conditions and the following
> + * disclaimer in the documentation and/or other materials
> + * provided with the distribution.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +#ifndef __RK1608_H__
> +#define __RK1608_H__
> +
> +#include <linux/types.h>
> +#include <linux/string.h>
> +#include <linux/spi/spi.h>
> +#include "linux/i2c.h"
> +
> +#define RK1608_OP_TRY_MAX 3
> +#define RK1608_OP_TRY_DELAY 10
> +#define RK1608_CMD_WRITE 0x00000011
> +#define RK1608_CMD_WRITE_REG0 0X00010011
> +#define RK1608_CMD_WRITE_REG1 0X00020011
> +#define RK1608_CMD_READ 0x00000077
> +#define RK1608_CMD_READ_BEGIN 0x000000aa
> +#define RK1608_CMD_QUERY 0x000000ff
> +#define RK1608_CMD_QUERY_REG2 0x000001ff
> +#define RK1608_STATE_ID_MASK (0xffff0000)
> +#define RK1608_STATE_ID (0X16080000)
> +#define RK1608_STATE_MASK (0x0000ffff)
> +
> +#define BOOT_REQUEST_ADDR 0x18000010
> +#define RK1608_HEAD_ADDR 0x60000000
> +#define RK1608_FW_NAME "rk1608.rkl"
> +#define RK1608_S_MSG_QUEUE_ADDR 0x60050010
> +#define RK1608_PMU_SYS_REG0 0x120000f0
> +#define RK1608_MSG_QUEUE_OK_MASK 0xffff0001
> +#define RK1608_MSG_QUEUE_OK_TAG 0x16080001
> +#define RK1608_MAX_OP_BYTES 60000
> +
> +#define RK1608_WINDOW_HEIGHT_DEF 480
> +#define RK1608_WINDOW_WIDTH_DEF 640
> +
> +#define BOOT_FLAG_CRC (0x01 << 0)
> +#define BOOT_FLAG_EXE (0x01 << 1)
> +#define BOOT_FLAG_LOAD_PMEM (0x01 << 2)
> +#define BOOT_FLAG_ACK (0x01 << 3)
> +#define BOOT_FLAG_READ_WAIT (0x01 << 4)
> +#define BOOT_FLAG_BOOT_REQUEST (0x01 << 5)
> +
> +#define DEBUG_DUMP_ALL_SEND_RECV_MSG 0
> +#define RK1608_MCLK_RATE (24 * 1000 * 1000ul)
> +#define SENSOR_TIMEOUT 1000
> +#define GRF_BASE_ADDR 0xff770000
> +#define GRF_GPIO2B_IOMUX 0x0014
> +#define GRF_IO_VSEL 0x0380
> +#define OPM_SLAVE_MODE 0X100000
> +#define RSD_SEL_2CYC 0X008000
> +#define DFS_SEL_16BIT 0X000002
> +#define SPI_CTRL0 0x11060000
> +#define SPI_ENR 0x11060008
> +#define CRUPMU_CLKSEL14_CON 0x12008098
> +#define PMUGRF_GPIO1A_E 0x12030040
> +#define PMUGRF_GPIO1B_E 0x12030044
> +#define BIT7_6_SEL_8MA 0xf000a000
> +#define BIT1_0_SEL_8MA 0x000f000a
> +#define SPI0_PLL_SEL_APLL 0xff004000
> +#define INVALID_ID -1
> +#define RK1608_MAX_SEC_NUM 10
> +
> +#ifndef MIN
> +#define MIN(a, b) ((a) < (b) ? (a) : (b))
> +#endif
> +
> +#ifndef MSB2LSB32
> +#define MSB2LSB32(x) ((((u32)x & 0x80808080) >> 7) | \
> + (((u32)x & 0x40404040) >> 5) | \
> + (((u32)x & 0x20202020) >> 3) | \
> + (((u32)x & 0x10101010) >> 1) | \
> + (((u32)x & 0x08080808) << 1) | \
> + (((u32)x & 0x04040404) << 3) | \
> + (((u32)x & 0x02020202) << 5) | \
> + (((u32)x & 0x01010101) << 7))
> +#endif
> +
> +struct rk1608_section {
> + union {
> + u32 offset;
> + u32 wait_value;
> + };
> + u32 size;
> + union {
> + u32 load_addr;
> + u32 wait_addr;
> + };
> + u16 wait_time;
> + u16 timeout;
> + u16 crc_16;
> + u8 flag;
> + u8 type;
> +};
> +
> +struct rk1608_header {
> + char version[32];
> + u32 header_size;
> + u32 section_count;
> + struct rk1608_section sections[RK1608_MAX_SEC_NUM];
> +};
> +
> +struct rk1608_boot_req {
> + u32 flag;
> + u32 load_addr;
> + u32 boot_len;
> + u8 status;
> + u8 dummy[2];
> + u8 cmd;
> +};
> +
> +struct rk1608_msg_queue {
> + u32 buf_head; /* msg buffer head */
> + u32 buf_tail; /* msg buffer tail */
> + u32 cur_send; /* current msg send postition */
> + u32 cur_recv; /* current msg receive position */
> +};
> +
> +struct msg {
> + u32 size; /* unit 4 bytes */
> + u16 type; /* msg identification */
> + s8 camera_id;
> + s8 sync;
> +};
> +
> +enum {
> + /** AP -> RK1608
> + * 1 msg of sensor
> + */
> + id_msg_init_sensor_t = 0x0001,
> + id_msg_set_input_size_t,
> + id_msg_set_output_size_t,
> + id_msg_set_stream_in_on_t,
> + id_msg_set_stream_in_off_t,
> + id_msg_set_stream_out_on_t,
> + id_msg_set_stream_out_off_t,
> +
> + /** AP -> RK1608
> + * 2 msg of take picture
> + */
> + id_msg_take_picture_t = 0x0021,
> + id_msg_take_picture_done_t,
> +
> + /** AP -> RK1608
> + * 3 msg of realtime parameter
> + */
> + id_msg_rt_args_t = 0x0031,
> +
> + /** AP -> RK1608
> + * 4 msg of power manager
> + */
> + id_msg_set_sys_mode_bypass_t = 0x0200,
> + id_msg_set_sys_mode_standby_t,
> + id_msg_set_sys_mode_idle_enable_t,
> + id_msg_set_sys_mode_idle_disable_t,
> + id_msg_set_sys_mode_slave_rk1608_on_t,
> + id_msg_set_sys_mode_slave_rk1608_off_t,
> +
> + /** AP -> RK1608
> + * 5 msg of debug config
> + */
> + id_msg_set_log_level_t = 0x0250,
> +
> + /** RK1608 -> AP
> + * 6 response of sensor msg
> + */
> + id_msg_init_sensor_ret_t = 0x0301,
> + id_msg_set_input_size_ret_t,
> + id_msg_set_output_size_ret_t,
> + id_msg_set_stream_in_on_ret_t,
> + id_msg_set_stream_in_off_ret_t,
> + id_msg_set_stream_out_on_ret_t,
> + id_msg_set_stream_out_off_ret_t,
> +
> + /** RK1608 -> AP
> + * 7 response of take picture msg
> + */
> + id_msg_take_picture_ret_t = 0x0320,
> + id_msg_take_picture_done_ret_t,
> +
> + /** RK1608 -> AP
> + * 8 response of realtime parameter msg
> + */
> + id_msg_rt_args_ret_t = 0x0330,
> +
> + /*rk1608 -> ap*/
> + id_msg_do_i2c_t = 0x0390,
> + /*ap -> rk1608*/
> + id_msg_do_i2c_ret_t,
> +
> + /** RK1608 -> AP
> + * 9 msg of print log
> + */
> + id_msg_rk1608_log_t = 0x0400,
> +
> + /* dsi2csi dump */
> + id_msg_dsi2sci_rgb_dump_t = 0x6000,
> + id_msg_dsi2sci_nv12_dump_t = 0x6001,
> +
> + /** RK1608 -> AP
> + * 10 msg of xfile
> + */
> + id_msg_xfile_import_t = 0x8000 + 0x0600,
> + id_msg_xfile_export_t,
> + id_msg_xfile_mkdir_t
> +};
> +
> +struct msg_rk1608_log_t {
> + u32 size;
> + u16 type;
> + s8 core_id;
> + s8 log_level;
> +};
> +
> +/**
> + * rk1608_write - RK1608 synchronous write
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_write(struct spi_device *spi, s32 addr,
> + const s32 *data, size_t data_len);
> +
> +/**
> + * rk1608_safe_write - RK1608 synchronous write with state check
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else operation state code.
> + */
> +int rk1608_safe_write(struct spi_device *spi,
> + s32 addr, const s32 *data, size_t data_len);
> +
> +/**
> + * rk1608_read - RK1608 synchronous read
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer [out]
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_read(struct spi_device *spi, s32 addr,
> + s32 *data, size_t data_len);
> +
> +/**
> + * rk1608_safe_read - RK1608 synchronous read with state check
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer [out]
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else operation state code.
> + */
> +int rk1608_safe_read(struct spi_device *spi,
> + s32 addr, s32 *data, size_t data_len);
> +
> +/**
> + * rk1608_operation_query - RK1608 last operation state query
> + *
> + * @spi: spi device
> + * @state: last operation state [out]
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_operation_query(struct spi_device *spi, s32 *state);
> +
> +/**
> + * rk1608_interrupt_request - RK1608 request a rk1608 interrupt
> + *
> + * @spi: spi device
> + * @interrupt_num: interrupt identification
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_interrupt_request(struct spi_device *spi,
> + s32 interrupt_num);
> +
> +static int rk1608_read_wait(struct spi_device *spi,
> + const struct rk1608_section *sec);
> +
> +static int rk1608_boot_request(struct spi_device *spi,
> + const struct rk1608_section *sec);
> +
> +static int rk1608_download_section(struct spi_device *spi, const u8 *data,
> + const struct rk1608_section *sec);
> +/**
> + * rk1608_download_fw: - rk1608 firmware download through spi
> + *
> + * @spi: spi device
> + * @fw_name: name of firmware file, NULL for default firmware name
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + **/
> +int rk1608_download_fw(struct spi_device *spi, const char *fw_name);
> +
> +/**
> + * rk1608_msq_read_head - read rk1608 msg queue head
> + *
> + * @spi: spi device
> + * @addr: msg queue head addr
> + * @m: msg queue pointer
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_msq_read_head(struct spi_device *spi,
> + u32 addr, struct rk1608_msg_queue *q);
> +
> +/**
> + * rk1608_msq_recv_msg - receive a msg from RK1608 -> AP msg queue
> + *
> + * @q: msg queue
> + * @m: a msg pointer buf [out]
> + *
> + * need call rk1608_msq_free_received_msg to free msg after msg use done
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_msq_recv_msg(struct spi_device *spi, struct msg **m);
> +#endif
> --
> 2.7.4
>
>
Please rebase your kernel tree to linux-next or media_tree, not
rockchip bsp kernel.
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/
https://git.linuxtv.org/
^ permalink raw reply
* Re: [GIT PULL 2/2] Rockchip dts64 changes for 4.16
From: Arnd Bergmann @ 2017-12-21 20:38 UTC (permalink / raw)
To: Heiko Stuebner; +Cc: open list:ARM/Rockchip SoC support, arm-soc, Linux ARM
In-Reply-To: <2062410.Qz0JYku65n@phil>
On Thu, Dec 21, 2017 at 7:31 PM, Heiko Stuebner <heiko@sntech.de> wrote:
> The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:
>
> Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)
>
> are available in the git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git tags/v4.16-rockchip-dts64-1
>
> for you to fetch changes up to 13bc2c0a6a14f430abaa6a859792418644b7febd:
>
> arm64: dts: rockchip: Add efuse device node for RK3328 SoC (2017-12-20 13:12:13 +0100)
>
> ----------------------------------------------------------------
> General RK3399 gets Mipi nodes, fixes for usb3 support and better support
> for the type-c phys. The Kevin Chromebooks based on rk3399 now can use their
> internal edp displays. RK3328 gets its efuse node and Mali450 gpu node,
> which actually produces already some nice results with the WIP Lima driver.
Pulled into next/dt, thanks!
Arnd
^ permalink raw reply
* Re: [GIT PULL 1/2] Rockchip dts32 changes for 4.16
From: Arnd Bergmann @ 2017-12-21 20:37 UTC (permalink / raw)
To: Heiko Stuebner; +Cc: open list:ARM/Rockchip SoC support, arm-soc, Linux ARM
In-Reply-To: <2013174.TmhWXT9G0E@phil>
On Thu, Dec 21, 2017 at 7:30 PM, Heiko Stuebner <heiko@sntech.de> wrote:
> Hi Arnd, Kevin, Olof,
>
> please find below and in the next mail Rockchip changes for 4.16.
> The volume especially on 32bit is quite low this time, possibly due
> to the holiday season. So if nothing looks bad, please pull
>
> Thanks and merry xmas
> Heiko
>
> The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:
>
> Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)
>
> are available in the git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git tags/v4.16-rockchip-dts32-1
>
> for you to fetch changes up to fe8fd85a23a746bd7f0a14e5dcd55c9bc517a055:
>
> ARM: dts: rockchip: add reset property for rk3066a-rayeager emac phy (2017-12-16 21:09:37 +0100)
>
Pulled into next/dt, thanks!
Arnd
^ permalink raw reply
* Re: [PATCH 1/2] clk: rename clk_core_get_boundaries() to clk_hw_get_boundaries() and expose
From: Stephen Boyd @ 2017-12-21 20:07 UTC (permalink / raw)
To: Alexander Kochetkov
Cc: linux-clk, linux-kernel, linux-arm-kernel, linux-rockchip,
Michael Turquette, Heiko Stuebner, Elaine Zhang
In-Reply-To: <1513872282-5370-2-git-send-email-al.kochet@gmail.com>
On 12/21, Alexander Kochetkov wrote:
> In order to provide a way to know clock limits to clock providers.
>
> The patch is needed for fixing commit 5d890c2df900 ("clk: rockchip:
> add special approximation to fix up fractional clk's jitter").
> Custom approximation function introduced by the patch, can
> select frequency rate larger than one configured using
> clk_set_max_rate().
>
> Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
> ---
Can you convert to the determine_rate op instead of round_rate?
That function should tell you the min/max limits so that you
don't need to query that information from the core.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [GIT PULL 2/2] Rockchip dts64 changes for 4.16
From: Heiko Stuebner @ 2017-12-21 18:31 UTC (permalink / raw)
To: arm; +Cc: linux-rockchip, linux-arm-kernel
In-Reply-To: <2013174.TmhWXT9G0E@phil>
The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:
Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git tags/v4.16-rockchip-dts64-1
for you to fetch changes up to 13bc2c0a6a14f430abaa6a859792418644b7febd:
arm64: dts: rockchip: Add efuse device node for RK3328 SoC (2017-12-20 13:12:13 +0100)
----------------------------------------------------------------
General RK3399 gets Mipi nodes, fixes for usb3 support and better support
for the type-c phys. The Kevin Chromebooks based on rk3399 now can use their
internal edp displays. RK3328 gets its efuse node and Mali450 gpu node,
which actually produces already some nice results with the WIP Lima driver.
----------------------------------------------------------------
Brian Norris (1):
arm64: dts: rockchip: add rk3399 DSI0 reset
Enric Balletbo i Serra (5):
arm64: dts: rockchip: add pd_usb3 power-domain node for rk3399
arm64: dts: rockchip: add the aclk_usb3 clocks for USB3 on rk3399
arm64: dts: rockchip: add reset property for dwc3 controllers on rk3399
arm64: dts: rockchip: add usb3-phy otg-port support for rk3399
arm64: dts: rockchip: add extcon nodes and enable tcphy rk3399-gru
Finley Xiao (1):
arm64: dts: rockchip: Add efuse device node for RK3328 SoC
Heiko Stuebner (2):
dt-bindings: gpu: mali-utgard: add rockchip,rk3328-mali compatible
arm64: dts: rockchip: add rk3328 mali gpu node
Jeffy Chen (1):
arm64: dts: rockchip: Enable edp disaplay on kevin
Nickey Yang (2):
arm64: dts: rockchip: add mipi_dsi1 support for rk3399
arm64: dts: rockchip: update mipi cells for RK3399
.../devicetree/bindings/gpu/arm,mali-utgard.txt | 1 +
arch/arm64/boot/dts/rockchip/rk3328.dtsi | 47 ++++++++++++
arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts | 29 ++++++++
arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 42 +++++++++++
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 85 +++++++++++++++++++---
5 files changed, 195 insertions(+), 9 deletions(-)
^ permalink raw reply
* [GIT PULL 1/2] Rockchip dts32 changes for 4.16
From: Heiko Stuebner @ 2017-12-21 18:30 UTC (permalink / raw)
To: arm; +Cc: linux-rockchip, linux-arm-kernel
Hi Arnd, Kevin, Olof,
please find below and in the next mail Rockchip changes for 4.16.
The volume especially on 32bit is quite low this time, possibly due
to the holiday season. So if nothing looks bad, please pull
Thanks and merry xmas
Heiko
The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:
Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git tags/v4.16-rockchip-dts32-1
for you to fetch changes up to fe8fd85a23a746bd7f0a14e5dcd55c9bc517a055:
ARM: dts: rockchip: add reset property for rk3066a-rayeager emac phy (2017-12-16 21:09:37 +0100)
----------------------------------------------------------------
Just the reset property for the rk3066a-rayeager emac phy
----------------------------------------------------------------
Chris Zhong (1):
ARM: dts: rockchip: add reset property for rk3066a-rayeager emac phy
arch/arm/boot/dts/rk3066a-rayeager.dts | 1 +
1 file changed, 1 insertion(+)
^ permalink raw reply
* [PATCH 2/2] clk: rockchip: limit clock rate in the rockchip_fractional_approximation()
From: Alexander Kochetkov @ 2017-12-21 16:04 UTC (permalink / raw)
To: linux-clk, linux-kernel, linux-arm-kernel, linux-rockchip
Cc: Michael Turquette, Elaine Zhang, Stephen Boyd, Heiko Stuebner,
Alexander Kochetkov
In-Reply-To: <1513872282-5370-1-git-send-email-al.kochet@gmail.com>
rockchip_fractional_approximation() can choose clock rate that can
be larger than one configured using clk_set_max_rate(). Request
to setup correct clock rate whose parent rate will be adjusted
to out of range value will fail with -EINVAL.
Fixes: commit 5d890c2df900 ("clk: rockchip: add special approximation
to fix up fractional clk's jitter").
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
drivers/clk/rockchip/clk.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 35dbd63..3c1fb0d 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -175,6 +175,7 @@ static void rockchip_fractional_approximation(struct clk_hw *hw,
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
unsigned long p_rate, p_parent_rate;
+ unsigned long min_rate = 0, max_rate = 0;
struct clk_hw *p_parent;
unsigned long scale;
@@ -182,6 +183,12 @@ static void rockchip_fractional_approximation(struct clk_hw *hw,
if ((rate * 20 > p_rate) && (p_rate % rate != 0)) {
p_parent = clk_hw_get_parent(clk_hw_get_parent(hw));
p_parent_rate = clk_hw_get_rate(p_parent);
+ clk_hw_get_boundaries(clk_hw_get_parent(hw),
+ &min_rate, &max_rate);
+ if (p_parent_rate < min_rate)
+ p_parent_rate = min_rate;
+ if (p_parent_rate > max_rate)
+ p_parent_rate = max_rate;
*parent_rate = p_parent_rate;
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 1/2] clk: rename clk_core_get_boundaries() to clk_hw_get_boundaries() and expose
From: Alexander Kochetkov @ 2017-12-21 16:04 UTC (permalink / raw)
To: linux-clk-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Michael Turquette, Elaine Zhang, Stephen Boyd, Heiko Stuebner,
Alexander Kochetkov
In-Reply-To: <1513872282-5370-1-git-send-email-al.kochet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
In order to provide a way to know clock limits to clock providers.
The patch is needed for fixing commit 5d890c2df900 ("clk: rockchip:
add special approximation to fix up fractional clk's jitter").
Custom approximation function introduced by the patch, can
select frequency rate larger than one configured using
clk_set_max_rate().
Signed-off-by: Alexander Kochetkov <al.kochet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
drivers/clk/clk.c | 14 ++++++++------
include/linux/clk-provider.h | 2 ++
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index c8d83ac..8943aac 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -421,10 +421,11 @@ struct clk *__clk_lookup(const char *name)
return !core ? NULL : core->hw->clk;
}
-static void clk_core_get_boundaries(struct clk_core *core,
- unsigned long *min_rate,
- unsigned long *max_rate)
+void clk_hw_get_boundaries(struct clk_hw *hw,
+ unsigned long *min_rate,
+ unsigned long *max_rate)
{
+ struct clk_core *core = hw->core;
struct clk *clk_user;
*min_rate = core->min_rate;
@@ -436,6 +437,7 @@ static void clk_core_get_boundaries(struct clk_core *core,
hlist_for_each_entry(clk_user, &core->clks, clks_node)
*max_rate = min(*max_rate, clk_user->max_rate);
}
+EXPORT_SYMBOL_GPL(clk_hw_get_boundaries);
void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
unsigned long max_rate)
@@ -894,7 +896,7 @@ unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate)
int ret;
struct clk_rate_request req;
- clk_core_get_boundaries(hw->core, &req.min_rate, &req.max_rate);
+ clk_hw_get_boundaries(hw, &req.min_rate, &req.max_rate);
req.rate = rate;
ret = clk_core_round_rate_nolock(hw->core, &req);
@@ -924,7 +926,7 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
clk_prepare_lock();
- clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate);
+ clk_hw_get_boundaries(clk->core->hw, &req.min_rate, &req.max_rate);
req.rate = rate;
ret = clk_core_round_rate_nolock(clk->core, &req);
@@ -1353,7 +1355,7 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
if (parent)
best_parent_rate = parent->rate;
- clk_core_get_boundaries(core, &min_rate, &max_rate);
+ clk_hw_get_boundaries(core->hw, &min_rate, &max_rate);
/* find the closest rate and parent clk/rate */
if (core->ops->determine_rate) {
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 5100ec1..2f10999 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -755,6 +755,8 @@ int __clk_mux_determine_rate_closest(struct clk_hw *hw,
void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent);
void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
unsigned long max_rate);
+void clk_hw_get_boundaries(struct clk_hw *hw, unsigned long *min_rate,
+ unsigned long *max_rate);
static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src)
{
--
1.7.9.5
^ permalink raw reply related
* [PATCH 0/2] Fix clock rate in the rockchip_fractional_approximation()
From: Alexander Kochetkov @ 2017-12-21 16:04 UTC (permalink / raw)
To: linux-clk, linux-kernel, linux-arm-kernel, linux-rockchip
Cc: Michael Turquette, Elaine Zhang, Stephen Boyd, Heiko Stuebner,
Alexander Kochetkov
Hello!
Here are two patches fixing issue in the rockchip_fractional_approximation().
rockchip_fractional_approximation() can select clock rate whose
value will violate clock limit settings (i.e. one configured with
clk_set_max_rate() and clk_set_min_rate()).
rockchip_fractional_approximation() was introduced by commit 5d890c2df900
("clk: rockchip: add special approximation to fix up fractional clk's jitter")
whose description states that setting denominator 20 times larger than
numerator will generate precise clock frequency. It's strange, because
on my custom rk3188-based board and on radxa rock I've observed strange
hardware issues. I2S, for example, sometimes doesn't setup correct
rate on external SCLK_I2S0. UART0, for example, started to receive '\0'
characters instead of valid symbols, signals on UART_RX was good.
So, I use clk_set_max_rate() to limit max value of i2s0_pre, uart0_pre,
uart1_pre, uart2_pre, uart3_pre to value of aclk_cpu_pre. That fixes
strange I2S and UART issues for me. If that make sense, than I can
send the patch. But it will logically conflict with commit 5d890c2df900
("clk: rockchip: add special approximation to fix up fractional clk's jitter").
Alexander Kochetkov (2):
clk: rename clk_core_get_boundaries() to clk_hw_get_boundaries() and
expose
clk: rockchip: limit clock rate in the
rockchip_fractional_approximation()
drivers/clk/clk.c | 14 ++++++++------
drivers/clk/rockchip/clk.c | 7 +++++++
include/linux/clk-provider.h | 2 ++
3 files changed, 17 insertions(+), 6 deletions(-)
--
1.7.9.5
^ permalink raw reply
* Re: [GIT PULL 2/2] Rockchip dts64 fixes for 4.15
From: Arnd Bergmann @ 2017-12-21 16:01 UTC (permalink / raw)
To: Heiko Stuebner; +Cc: open list:ARM/Rockchip SoC support, arm-soc, Linux ARM
In-Reply-To: <2690908.kxVfvpsLEa@phil>
On Wed, Dec 13, 2017 at 7:57 AM, Heiko Stuebner <heiko@sntech.de> wrote:
> The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:
>
> Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)
>
> are available in the git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git tags/v4.15-rockchip-dts64fixes-1
>
> for you to fetch changes up to bc631943faba6fc3f755748091ada31798fb7d50:
>
> arm64: dts: rockchip: limit rk3328-rock64 gmac speed to 100MBit for now (2017-12-06 01:14:20 +0100)
>
> ----------------------------------------------------------------
> Another trailing interrupt-cell 0 removed.
>
> Removed as well got the vdd_log regulator from the rk3399-puma board.
> While it is there, the absence of any user makes it prone to configuration
> problems when the pwm-regulator takes over the boot-up default and wiggles
> settings there. Case in question was the PCIe host not working anymore.
> With vdd_log removed for the time being, PCIe on Puma works again.
>
> And a second stopgap is limiting the speed of the gmac on the rk3328-rock64
> to 100MBit. While the hardware can reach 1GBit, currently it is not stable.
> Limiting it to 100MBit for the time being allows nfsroots to be used again
> until the problem is identified.
Pulled into fixes, thanks!
Arnd
^ permalink raw reply
* Re: [GIT PULL 1/2] Rockchip dts32 fixes for 4.15
From: Arnd Bergmann @ 2017-12-21 15:59 UTC (permalink / raw)
To: Heiko Stuebner; +Cc: open list:ARM/Rockchip SoC support, arm-soc, Linux ARM
In-Reply-To: <6469701.IcmEy0bVhn@phil>
On Wed, Dec 13, 2017 at 7:56 AM, Heiko Stuebner <heiko@sntech.de> wrote:
> Hi Arnd, Kevin, Olof,
>
> please find below and in the next mail some fixes for things cropping up
> recently, which may hopefully go into 4.15-rc.
Pulled into fixes, thanks!
Arnd
^ permalink raw reply
* Re: [PATCH v7 1/3] drm/bridge/synopsys: dsi: stop clobbering drvdata
From: Philippe CORNU @ 2017-12-21 14:59 UTC (permalink / raw)
To: Nickey Yang, robh+dt@kernel.org, heiko@sntech.de,
mark.rutland@arm.com, airlied@linux.ie
Cc: hl@rock-chips.com, briannorris@chromium.org, hoegsberg@gmail.com,
linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org,
Yannick FERTRE, linux-rockchip@lists.infradead.org,
mka@chromium.org, laurent.pinchart@ideasonboard.com,
zyw@rock-chips.com, xbl@rock-chips.com
In-Reply-To: <1513041045-12808-2-git-send-email-nickey.yang@rock-chips.com>
Hi Nickey,
On 12/12/2017 02:10 AM, Nickey Yang wrote:
> From: Brian Norris <briannorris@chromium.org>
>
> Bridge drivers/helpers shouldn't be clobbering the drvdata, since a
> parent driver might need to own this. Instead, let's return our
> 'dw_mipi_dsi' object and have callers pass that back to us for removal.
>
> Signed-off-by: Brian Norris <briannorris@chromium.org>
> Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
> Link:https://patchwork.kernel.org/patch/10078493/
>
> ---
> Changes
>
> v4:
> - Add From tag,update subject line
> - keep patch "drm/stm: dsi: Adjust dw_mipi_dsi_probe and remove"
> in this piece together.
>
> v5:
> - remove Review & Ack tag
> - fix remove() directly referencing the static
> dw_mipi_dsi_stm_plat_data struct.
>
> v7:
> - add missing platform_set_drvdata in stm part.
>
> drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 36 ++++++++++-----------------
> drivers/gpu/drm/stm/dw_mipi_dsi-stm.c | 12 ++++++---
> include/drm/bridge/dw_mipi_dsi.h | 17 ++++++++-----
> 3 files changed, 32 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index d9cca4f..c39c7dc 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -922,8 +922,6 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
> dsi->bridge.of_node = pdev->dev.of_node;
> #endif
>
> - dev_set_drvdata(dev, dsi);
> -
> return dsi;
> }
>
> @@ -935,23 +933,16 @@ static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
> /*
> * Probe/remove API, used from platforms based on the DRM bridge API.
> */
> -int dw_mipi_dsi_probe(struct platform_device *pdev,
> - const struct dw_mipi_dsi_plat_data *plat_data)
> +struct dw_mipi_dsi *
> +dw_mipi_dsi_probe(struct platform_device *pdev,
> + const struct dw_mipi_dsi_plat_data *plat_data)
> {
> - struct dw_mipi_dsi *dsi;
> -
> - dsi = __dw_mipi_dsi_probe(pdev, plat_data);
> - if (IS_ERR(dsi))
> - return PTR_ERR(dsi);
> -
> - return 0;
> + return __dw_mipi_dsi_probe(pdev, plat_data);
> }
> EXPORT_SYMBOL_GPL(dw_mipi_dsi_probe);
>
> -void dw_mipi_dsi_remove(struct platform_device *pdev)
> +void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
> {
> - struct dw_mipi_dsi *dsi = platform_get_drvdata(pdev);
> -
> mipi_dsi_host_unregister(&dsi->dsi_host);
>
> __dw_mipi_dsi_remove(dsi);
> @@ -961,31 +952,30 @@ void dw_mipi_dsi_remove(struct platform_device *pdev)
> /*
> * Bind/unbind API, used from platforms based on the component framework.
> */
> -int dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
> - const struct dw_mipi_dsi_plat_data *plat_data)
> +struct dw_mipi_dsi *
> +dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
> + const struct dw_mipi_dsi_plat_data *plat_data)
> {
> struct dw_mipi_dsi *dsi;
> int ret;
>
> dsi = __dw_mipi_dsi_probe(pdev, plat_data);
> if (IS_ERR(dsi))
> - return PTR_ERR(dsi);
> + return dsi;
>
> ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
> if (ret) {
> - dw_mipi_dsi_remove(pdev);
> + dw_mipi_dsi_remove(dsi);
> DRM_ERROR("Failed to initialize bridge with drm\n");
> - return ret;
> + return ERR_PTR(ret);
> }
>
> - return 0;
> + return dsi;
> }
> EXPORT_SYMBOL_GPL(dw_mipi_dsi_bind);
>
> -void dw_mipi_dsi_unbind(struct device *dev)
> +void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi)
> {
> - struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
> -
> __dw_mipi_dsi_remove(dsi);
> }
> EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind);
> diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
> index e5b6310..c1ed691 100644
> --- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
> +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
> @@ -66,6 +66,7 @@ enum dsi_color {
> struct dw_mipi_dsi_stm {
> void __iomem *base;
> struct clk *pllref_clk;
> + struct dw_mipi_dsi *dmd;
> };
>
> static inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val)
> @@ -290,6 +291,8 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
> if (!dsi)
> return -ENOMEM;
>
> + platform_set_drvdata(pdev, dsi);
> +
Why don't you simply use the original patch from Brian.
With your last update, platform_set_drvdata() is called early during the
stm_probe() and this is not the "classic" way: most of the time we
prefer calling platform_set_drvdata() at the end of the probe (or as
close as possible of the end of the probe). I took few minutes to look
into drm directory and it seems to be the classic way for most of the
drivers...
> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> if (!res) {
> DRM_ERROR("Unable to get resource\n");
> @@ -318,10 +321,11 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
> dw_mipi_dsi_stm_plat_data.base = dsi->base;
> dw_mipi_dsi_stm_plat_data.priv_data = dsi;
>
> - ret = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data);
> - if (ret) {
> + dsi->dmd = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data);
> + if (IS_ERR(dsi->dmd)) {
> DRM_ERROR("Failed to initialize mipi dsi host\n");
> clk_disable_unprepare(dsi->pllref_clk);
> + return PTR_ERR(dsi->dmd);
> }
>
> return ret;
Moreover, I do prefer the "return 0;" from the Brian original version
because "ret" here is far from its usage (from clk_prepare_enable())
with this new patch.
So, once again, I really prefer the Brian original version, on which you
already got my Tested-By & Acked-by.
Is there any reason why you did not use the original patch?
Many thanks,
Philippe :-)
Note: I will be ooo next 2 weeks.
> @@ -329,10 +333,10 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
>
> static int dw_mipi_dsi_stm_remove(struct platform_device *pdev)
> {
> - struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
> + struct dw_mipi_dsi_stm *dsi = platform_get_drvdata(pdev);
>
> clk_disable_unprepare(dsi->pllref_clk);
> - dw_mipi_dsi_remove(pdev);
> + dw_mipi_dsi_remove(dsi->dmd);
>
> return 0;
> }
> diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h
> index 9b30fec..d9c6d54 100644
> --- a/include/drm/bridge/dw_mipi_dsi.h
> +++ b/include/drm/bridge/dw_mipi_dsi.h
> @@ -10,6 +10,8 @@
> #ifndef __DW_MIPI_DSI__
> #define __DW_MIPI_DSI__
>
> +struct dw_mipi_dsi;
> +
> struct dw_mipi_dsi_phy_ops {
> int (*init)(void *priv_data);
> int (*get_lane_mbps)(void *priv_data, struct drm_display_mode *mode,
> @@ -29,11 +31,14 @@ struct dw_mipi_dsi_plat_data {
> void *priv_data;
> };
>
> -int dw_mipi_dsi_probe(struct platform_device *pdev,
> - const struct dw_mipi_dsi_plat_data *plat_data);
> -void dw_mipi_dsi_remove(struct platform_device *pdev);
> -int dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
> - const struct dw_mipi_dsi_plat_data *plat_data);
> -void dw_mipi_dsi_unbind(struct device *dev);
> +struct dw_mipi_dsi *dw_mipi_dsi_probe(struct platform_device *pdev,
> + const struct dw_mipi_dsi_plat_data
> + *plat_data);
> +void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi);
> +struct dw_mipi_dsi *dw_mipi_dsi_bind(struct platform_device *pdev,
> + struct drm_encoder *encoder,
> + const struct dw_mipi_dsi_plat_data
> + *plat_data);
> +void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi);
>
> #endif /* __DW_MIPI_DSI__ */
>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [RFC PATCH v2 1/3] PCI: rockchip: Add support for pcie wake irq
From: Tony Lindgren @ 2017-12-20 19:19 UTC (permalink / raw)
To: Brian Norris
Cc: jeffy, linux-kernel, bhelgaas, shawn.lin, dianders,
Heiko Stuebner, linux-pci, linux-rockchip, linux-arm-kernel
In-Reply-To: <20171219004811.GA216620@google.com>
Hi,
* Brian Norris <briannorris@chromium.org> [171219 00:50]:
> On Wed, Aug 23, 2017 at 09:32:39AM +0800, Jeffy Chen wrote:
>
> Did this problem ever get resolved? To be clear, I believe the problem
> at hand is:
>
> (a) in suspend/resume (not runtime PM; we may not even have runtime PM
> support for most PCI devices)
It seems it should be enough to implement runtime PM in the PCI
controller. Isn't each PCI WAKE# line is wired from each PCI device
to the PCI controller?
Then the PCI controller can figure out from which PCI device the
WAKE# came from.
> Options I can think of:
> (1) implement runtime PM callbacks for all PCI devices, where we clear
> any PME status and ensure WAKE# stops asserting [1]
I don't think this is needed, it should be enough to have just
the PCI controller implement runtime PM :)
Regards,
Tony
^ permalink raw reply
* [PATCH 2/2] drm/rockchip: Remove analogix psr worker.
From: Enric Balletbo i Serra @ 2017-12-20 18:20 UTC (permalink / raw)
To: Mark Yao, David Airlie, Heiko Stuebner, Sean Paul
Cc: dri-devel, linux-arm-kernel, linux-rockchip, linux-kernel, wzz,
marcheu
In-Reply-To: <20171220182044.20331-1-enric.balletbo@collabora.com>
From: Sean Paul <seanpaul@chromium.org>
Now that the spinlocks and timers are gone, we can remove the psr
worker located in rockchip's analogix driver and do the enable/disable
directly. This should simplify the code and remove races on disable.
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 30 ++-----------------------
1 file changed, 2 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 93b7102..d32c9b3 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -71,10 +71,6 @@ struct rockchip_dp_device {
struct regmap *grf;
struct reset_control *rst;
- struct work_struct psr_work;
- struct mutex psr_lock;
- unsigned int psr_state;
-
const struct rockchip_dp_chip_data *data;
struct analogix_dp_plat_data plat_data;
@@ -83,27 +79,13 @@ struct rockchip_dp_device {
static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
{
struct rockchip_dp_device *dp = to_dp(encoder);
+ int ret;
if (!analogix_dp_psr_supported(dp->dev))
return;
DRM_DEV_DEBUG(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
- mutex_lock(&dp->psr_lock);
- if (enabled)
- dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
- else
- dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
-
- schedule_work(&dp->psr_work);
- mutex_unlock(&dp->psr_lock);
-}
-
-static void analogix_dp_psr_work(struct work_struct *work)
-{
- struct rockchip_dp_device *dp =
- container_of(work, typeof(*dp), psr_work);
- int ret;
ret = rockchip_drm_wait_vact_end(dp->encoder.crtc,
PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
@@ -112,12 +94,10 @@ static void analogix_dp_psr_work(struct work_struct *work)
return;
}
- mutex_lock(&dp->psr_lock);
- if (dp->psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+ if (enabled)
analogix_dp_enable_psr(dp->dev);
else
analogix_dp_disable_psr(dp->dev);
- mutex_unlock(&dp->psr_lock);
}
static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
@@ -134,8 +114,6 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
struct rockchip_dp_device *dp = to_dp(plat_data);
int ret;
- cancel_work_sync(&dp->psr_work);
-
ret = clk_prepare_enable(dp->pclk);
if (ret < 0) {
DRM_DEV_ERROR(dp->dev, "failed to enable pclk %d\n", ret);
@@ -379,10 +357,6 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
dp->plat_data.power_off = rockchip_dp_powerdown;
dp->plat_data.get_modes = rockchip_dp_get_modes;
- mutex_init(&dp->psr_lock);
- dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
- INIT_WORK(&dp->psr_work, analogix_dp_psr_work);
-
rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
--
2.9.3
^ permalink raw reply related
* [PATCH 1/2] drm/rockchip: Don't use atomic constructs for psr
From: Enric Balletbo i Serra @ 2017-12-20 18:20 UTC (permalink / raw)
To: Mark Yao, David Airlie, Heiko Stuebner, Sean Paul
Cc: wzz, linux-kernel, dri-devel, linux-rockchip, marcheu,
linux-arm-kernel
In-Reply-To: <20171220182044.20331-1-enric.balletbo@collabora.com>
From: Sean Paul <seanpaul@chromium.org>
Instead of using timer and spinlocks, use delayed_work and
mutexes for rockchip psr. This allows us to make blocking
calls when enabling/disabling psr (which is sort of important
given we're talking over dpcd to the display).
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +-
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 2 +-
drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 63 +++++++++++++----------------
3 files changed, 29 insertions(+), 38 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 76d63de..cd7ae12 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -134,7 +134,7 @@ static int rockchip_drm_bind(struct device *dev)
drm_dev->dev_private = private;
INIT_LIST_HEAD(&private->psr_list);
- spin_lock_init(&private->psr_list_lock);
+ mutex_init(&private->psr_list_lock);
ret = rockchip_drm_init_iommu(drm_dev);
if (ret)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 498dfbc..9c064a4 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -55,7 +55,7 @@ struct rockchip_drm_private {
struct mutex mm_lock;
struct drm_mm mm;
struct list_head psr_list;
- spinlock_t psr_list_lock;
+ struct mutex psr_list_lock;
};
int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
index 3acfd57..a3f6ec0 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
@@ -18,7 +18,7 @@
#include "rockchip_drm_drv.h"
#include "rockchip_drm_psr.h"
-#define PSR_FLUSH_TIMEOUT msecs_to_jiffies(100)
+#define PSR_FLUSH_TIMEOUT_MS 100
enum psr_state {
PSR_FLUSH,
@@ -30,11 +30,11 @@ struct psr_drv {
struct list_head list;
struct drm_encoder *encoder;
- spinlock_t lock;
+ struct mutex lock;
bool active;
enum psr_state state;
- struct timer_list flush_timer;
+ struct delayed_work flush_work;
void (*set)(struct drm_encoder *encoder, bool enable);
};
@@ -43,9 +43,8 @@ static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
{
struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
struct psr_drv *psr;
- unsigned long flags;
- spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ mutex_lock(&drm_drv->psr_list_lock);
list_for_each_entry(psr, &drm_drv->psr_list, list) {
if (psr->encoder->crtc == crtc)
goto out;
@@ -53,7 +52,7 @@ static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
psr = ERR_PTR(-ENODEV);
out:
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+ mutex_unlock(&drm_drv->psr_list_lock);
return psr;
}
@@ -94,23 +93,21 @@ static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state)
static void psr_set_state(struct psr_drv *psr, enum psr_state state)
{
- unsigned long flags;
-
- spin_lock_irqsave(&psr->lock, flags);
+ mutex_lock(&psr->lock);
psr_set_state_locked(psr, state);
- spin_unlock_irqrestore(&psr->lock, flags);
+ mutex_unlock(&psr->lock);
}
-static void psr_flush_handler(struct timer_list *t)
+static void psr_flush_handler(struct work_struct *work)
{
- struct psr_drv *psr = from_timer(psr, t, flush_timer);
- unsigned long flags;
+ struct psr_drv *psr = container_of(to_delayed_work(work),
+ struct psr_drv, flush_work);
/* If the state has changed since we initiated the flush, do nothing */
- spin_lock_irqsave(&psr->lock, flags);
+ mutex_lock(&psr->lock);
if (psr->state == PSR_FLUSH)
psr_set_state_locked(psr, PSR_ENABLE);
- spin_unlock_irqrestore(&psr->lock, flags);
+ mutex_unlock(&psr->lock);
}
/**
@@ -123,14 +120,13 @@ static void psr_flush_handler(struct timer_list *t)
int rockchip_drm_psr_activate(struct drm_crtc *crtc)
{
struct psr_drv *psr = find_psr_by_crtc(crtc);
- unsigned long flags;
if (IS_ERR(psr))
return PTR_ERR(psr);
- spin_lock_irqsave(&psr->lock, flags);
+ mutex_lock(&psr->lock);
psr->active = true;
- spin_unlock_irqrestore(&psr->lock, flags);
+ mutex_unlock(&psr->lock);
return 0;
}
@@ -146,15 +142,14 @@ EXPORT_SYMBOL(rockchip_drm_psr_activate);
int rockchip_drm_psr_deactivate(struct drm_crtc *crtc)
{
struct psr_drv *psr = find_psr_by_crtc(crtc);
- unsigned long flags;
if (IS_ERR(psr))
return PTR_ERR(psr);
- spin_lock_irqsave(&psr->lock, flags);
+ mutex_lock(&psr->lock);
psr->active = false;
- spin_unlock_irqrestore(&psr->lock, flags);
- del_timer_sync(&psr->flush_timer);
+ mutex_unlock(&psr->lock);
+ cancel_delayed_work_sync(&psr->flush_work);
return 0;
}
@@ -162,8 +157,7 @@ EXPORT_SYMBOL(rockchip_drm_psr_deactivate);
static void rockchip_drm_do_flush(struct psr_drv *psr)
{
- mod_timer(&psr->flush_timer,
- round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
+ schedule_delayed_work(&psr->flush_work, PSR_FLUSH_TIMEOUT_MS);
psr_set_state(psr, PSR_FLUSH);
}
@@ -201,12 +195,11 @@ void rockchip_drm_psr_flush_all(struct drm_device *dev)
{
struct rockchip_drm_private *drm_drv = dev->dev_private;
struct psr_drv *psr;
- unsigned long flags;
- spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ mutex_lock(&drm_drv->psr_list_lock);
list_for_each_entry(psr, &drm_drv->psr_list, list)
rockchip_drm_do_flush(psr);
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+ mutex_unlock(&drm_drv->psr_list_lock);
}
EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
@@ -223,7 +216,6 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
{
struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
struct psr_drv *psr;
- unsigned long flags;
if (!encoder || !psr_set)
return -EINVAL;
@@ -232,17 +224,17 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
if (!psr)
return -ENOMEM;
- timer_setup(&psr->flush_timer, psr_flush_handler, 0);
- spin_lock_init(&psr->lock);
+ INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler);
+ mutex_init(&psr->lock);
psr->active = true;
psr->state = PSR_DISABLE;
psr->encoder = encoder;
psr->set = psr_set;
- spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ mutex_lock(&drm_drv->psr_list_lock);
list_add_tail(&psr->list, &drm_drv->psr_list);
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+ mutex_unlock(&drm_drv->psr_list_lock);
return 0;
}
@@ -260,16 +252,15 @@ void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
{
struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
struct psr_drv *psr, *n;
- unsigned long flags;
- spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ mutex_lock(&drm_drv->psr_list_lock);
list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
if (psr->encoder == encoder) {
- del_timer(&psr->flush_timer);
+ cancel_delayed_work_sync(&psr->flush_work);
list_del(&psr->list);
kfree(psr);
}
}
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+ mutex_unlock(&drm_drv->psr_list_lock);
}
EXPORT_SYMBOL(rockchip_drm_psr_unregister);
--
2.9.3
^ permalink raw reply related
* [PATCH 0/2] drm/rockchip: Fix sleeping function called from invalid context
From: Enric Balletbo i Serra @ 2017-12-20 18:20 UTC (permalink / raw)
To: Mark Yao, David Airlie, Heiko Stuebner, Sean Paul
Cc: dri-devel, linux-arm-kernel, linux-rockchip, linux-kernel, wzz,
marcheu
Dear all,
After enable the debug option to check sleep inside atomic section I got
lots of messages from the drm/rockchip driver using current 4.15-rc4
BUG: sleeping function called from invalid context at kernel/locking/mutex.c:238
in_atomic(): 1, irqs_disabled(): 128, pid: 3457, name: Xorg
CPU: 3 PID: 3457 Comm: Xorg Tainted: G W 4.15.0-rc4+ #56
Hardware name: Google Kevin (DT)
Call trace:
dump_backtrace+0x0/0x1a8
show_stack+0x24/0x30
dump_stack+0xb8/0xf0
___might_sleep+0x110/0x140
__might_sleep+0x58/0x90
mutex_lock+0x2c/0x68
analogix_dp_psr_set+0x78/0x100
rockchip_drm_do_flush+0x6c/0x88
rockchip_drm_psr_flush_all+0x48/0x70
rockchip_drm_fb_dirty+0x20/0x30
drm_mode_dirtyfb_ioctl+0x1c4/0x1f8
drm_ioctl_kernel+0x74/0xd0
drm_ioctl+0x2b8/0x3c0
do_vfs_ioctl+0xb0/0x818
SyS_ioctl+0x94/0xa8
el0_svc_naked+0x20/0x24
The two patches in this patchset were sent by Sean Paul some time ago
([1][2]) but never landed in mainline, the patches in question can fix the
issue reported and I think that could be interesting include both in this
release cycle, hence I'm resending it. The patches were rebased on top of
mainline.
[1] https://patchwork.kernel.org/patch/9382847/
[2] https://patchwork.kernel.org/patch/9614679/
Best regards,
Enric
Sean Paul (2):
drm/rockchip: Don't use atomic constructs for psr
drm/rockchip: Remove analogix psr worker.
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 30 +-----------
drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +-
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 2 +-
drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 63 +++++++++++--------------
4 files changed, 31 insertions(+), 66 deletions(-)
--
2.9.3
^ permalink raw reply
* Re: [PATCH v4 11/16] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
From: Rob Herring @ 2017-12-20 18:16 UTC (permalink / raw)
To: Jacob Chen
Cc: linux-rockchip, linux-kernel, linux-arm-kernel, mchehab,
linux-media, sakari.ailus, hans.verkuil, tfiga, zhengsq,
laurent.pinchart, zyc, eddie.cai.linux, jeffy.chen, allon.huang,
devicetree, heiko, Joao.Pinto, Luis.Oliveira, Jose.Abreu,
Jacob Chen
In-Reply-To: <20171218121445.6086-8-jacob-chen@iotwrt.com>
On Mon, Dec 18, 2017 at 08:14:40PM +0800, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> Add DT bindings documentation for Rockchip MIPI D-PHY RX
>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> ---
> .../bindings/media/rockchip-mipi-dphy.txt | 88 ++++++++++++++++++++++
> 1 file changed, 88 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH v4 10/16] dt-bindings: Document the Rockchip ISP1 bindings
From: Rob Herring @ 2017-12-20 18:14 UTC (permalink / raw)
To: Jacob Chen
Cc: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
mchehab-DgEjT+Ai2ygdnm+yROfE0A,
linux-media-u79uwXL29TY76Z2rM5mHXA,
sakari.ailus-VuQAYsv1563Yd54FQh9/CA,
hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w, tfiga-F7+t8E8rja9g9hUCZPvPmw,
zhengsq-TNX95d0MmH7DzftRWevZcw,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
zyc-TNX95d0MmH7DzftRWevZcw,
eddie.cai.linux-Re5JQEeQqe8AvxtiuMwx3w,
jeffy.chen-TNX95d0MmH7DzftRWevZcw,
allon.huang-TNX95d0MmH7DzftRWevZcw,
devicetree-u79uwXL29TY76Z2rM5mHXA, heiko-4mtYJXux2i+zQB+pC5nmwQ,
Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
Luis.Oliveira-HKixBCOQz3hWk0Htik3J/w,
Jose.Abreu-HKixBCOQz3hWk0Htik3J/w, Jacob Chen
In-Reply-To: <20171218121445.6086-7-jacob-chen-fyOeoxGR3m/QT0dZR+AlfA@public.gmane.org>
On Mon, Dec 18, 2017 at 08:14:39PM +0800, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>
> Add DT bindings documentation for Rockchip ISP1
>
> Signed-off-by: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> ---
> .../devicetree/bindings/media/rockchip-isp1.txt | 69 ++++++++++++++++++++++
> 1 file changed, 69 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp1.txt
Reviewed-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 2/2] arm64: dts: rockchip: Add efuse device node for RK3328 SoC
From: Heiko Stuebner @ 2017-12-20 12:13 UTC (permalink / raw)
To: Finley Xiao
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, jay.xu-TNX95d0MmH7DzftRWevZcw,
huangtao-TNX95d0MmH7DzftRWevZcw, tony.xie-TNX95d0MmH7DzftRWevZcw,
cl-TNX95d0MmH7DzftRWevZcw
In-Reply-To: <1501817618-65504-3-git-send-email-finley.xiao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Am Freitag, 4. August 2017, 11:33:38 CET schrieb Finley Xiao:
> This patch adds an efuse node in the device tree for rk3228 SoC.
>
> Signed-off-by: Finley Xiao <finley.xiao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
applied for 4.16, after the driver change landed now.
Thanks
Heiko
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] drm/rockchip: analogix_dp: Ensure that the bridge is powered before poking it
From: Andrzej Hajda @ 2017-12-20 11:25 UTC (permalink / raw)
To: Marc Zyngier, Mark Yao, Heiko Stuebner
Cc: linux-rockchip, dri-devel, linux-arm-kernel
In-Reply-To: <17fd8819-2771-7dd2-03c1-ba23326961d8@arm.com>
On 19.12.2017 12:42, Marc Zyngier wrote:
> On 19/12/17 07:55, Andrzej Hajda wrote:
>> On 18.12.2017 12:28, Marc Zyngier wrote:
>>> Stopping the X display manager on a kevin platform results in the
>>> following crash:
>>>
>>> [ 674.833536] Synchronous External Abort: synchronous external abort (0x96000010) at 0xffff00000c970640
>>> [ 674.843886] Internal error: : 96000010 [#1] PREEMPT SMP
>>> [ 674.849744] Modules linked in:
>>> [ 674.849755] CPU: 1 PID: 86 Comm: kworker/1:1 Not tainted 4.15.0-rc3-00057-gff24f8cf492d-dirty #3
>>> [ 674.849760] detected fb_set_par error, error code: -16
>>> [ 674.849761] Hardware name: Google Kevin (DT)
>>> [ 674.849773] Workqueue: events analogix_dp_psr_work
>>> [ 674.849778] pstate: 60000005 (nZCv daif -PAN -UAO)
>>> [ 674.849784] pc : analogix_dp_send_psr_spd+0x8/0x168
>>> [ 674.849788] lr : analogix_dp_enable_psr+0x54/0x60
>>> [ 674.849789] sp : ffff000009b2bd60
>>> [ 674.849790] x29: ffff000009b2bd60 x28: 0000000000000000
>>> [ 674.849794] x27: ffff000009913d20 x26: ffff00000900fbf0
>>> [ 674.849797] x25: ffff8000f1b30000 x24: ffff8000f0c21d98
>>> [ 674.849800] x23: 0000000000000000 x22: ffff8000f7d3aa00
>>> [ 674.849803] x21: ffff8000f7d36980 x20: ffff8000f0c21c18
>>> [ 674.849806] x19: ffff8000f0c21db8 x18: 0000000000000001
>>> [ 674.849809] x17: 0000ffff89f2ed58 x16: ffff000008222908
>>> [ 674.849812] x15: 0000000000000000 x14: 0000000000000400
>>> [ 674.849815] x13: 0000000000000400 x12: 0000000000000000
>>> [ 674.849817] x11: 0000000000001414 x10: 0000000000000a00
>>> [ 674.849820] x9 : ffff000009b2bbb0 x8 : ffff8000f1b30a60
>>> [ 674.849823] x7 : 0000000000080000 x6 : 0000000000000001
>>> [ 674.849826] x5 : 0000000000000010 x4 : 0000000000000007
>>> [ 674.849829] x3 : 0000000000000002 x2 : ffff00000c970640
>>> [ 674.849832] x1 : ffff000009b2bd78 x0 : ffff8000f1624018
>>> [ 674.849836] Process kworker/1:1 (pid: 86, stack limit = 0x0000000083e5f7c3)
>>> [ 674.849838] Call trace:
>>> [ 674.849842] analogix_dp_send_psr_spd+0x8/0x168
>>> [ 674.849844] analogix_dp_psr_work+0x9c/0xa0
>>> [ 674.849849] process_one_work+0x1cc/0x328
>>> [ 674.849852] worker_thread+0x50/0x450
>>> [ 674.849856] kthread+0xf8/0x128
>>> [ 674.849860] ret_from_fork+0x10/0x18
>>> [ 674.849864] Code: b9000001 d65f03c0 f9445802 91190042 (b9400042)
>>>
>>> Further investigation show that this happens because the the workqueue
>>> races with the analogix_dp_bridge_disable() call from the core DRM code,
>>> and end up trying to write to the DP bridge that has already been powered
>>> down. This result is a very black screen, and a hard reset.
>>>
>>> Instead of counting on luck to keep the bridge alive, let's use the
>>> pm_runtime framework and take a reference on the device when we're about
>>> to poke it. That is a fairly big hammer, but one that allows the system
>>> to stay alive across dozens of X start/stop sequences.
>> Wouldn't be better to cancel the work in analogix_dp_bridge_disable, it
>> looks safer.
> Not sure. That would only cancel a single work that would be in flight
> right when we hit disable, but won't prevent the work from being queued
> right after the cancel.
>
> In summary, I think you're trading a race between pm_runtime_put_sync
> and analogix_dp_send_psr_spd for another between cancel_work_sync and
> analogix_dp_send_psr_spd. Also, I seem to remember that the disable can
> occur in its own work queue:
>
> commit_tail -> drm_atomic_helper_commit_modeset_disables ->
> disable_outputs -> drm_bridge_disable -> analogix_dp_bridge_disable
>
> making it racy by nature. But I'm no DRM expert (as you can probably tell).
>
> My approach is to guarantee that analogix_dp_send_psr_spd cannot fault
> due to the IP being powered off, which feels a bit more bullet proof.
I suspect the worker should not be executed during/after disable, at
least its body suggests it.
And if it will be guaranteed, runtime dance in the worker is pointless.
Regards
Andrzej
>
> Please shoot me down if I got it wrong!
>
> Thanks,
>
> M.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [PATCH] pinctrl: rockchip: enable clock when reading pin direction register
From: Linus Walleij @ 2017-12-20 8:01 UTC (permalink / raw)
To: Brian Norris
Cc: Heiko Stuebner, linux-gpio, Linux ARM,
open list:ARM/Rockchip SoC..., linux-kernel@vger.kernel.org,
Doug Anderson
In-Reply-To: <20171212174343.192017-1-briannorris@chromium.org>
On Tue, Dec 12, 2017 at 6:43 PM, Brian Norris <briannorris@chromium.org> wrote:
> We generally leave the GPIO clock disabled, unless an interrupt is
> requested or we're accessing IO registers. We forgot to do this for the
> ->get_direction() callback, which means we can sometimes [1] get
> incorrect results [2] from, e.g., /sys/kernel/debug/gpio.
>
> Enable the clock, so we get the right results!
>
> [1] Sometimes, because many systems have 1 or mor interrupt requested on
> each GPIO bank, so they always leave their clock on.
>
> [2] Incorrect, meaning the register returns 0, and so we interpret that
> as "input".
>
> Signed-off-by: Brian Norris <briannorris@chromium.org>
Patch applied with Heiko's review tag.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH for-4.15] ASoC: rt5514: don't assume rt5514 component was "attached"
From: Brian Norris @ 2017-12-19 17:31 UTC (permalink / raw)
To: Mark Brown
Cc: Cheng-yi Chiang, Oder Chiou, Bard Liao, Liam Girdwood,
Jaroslav Kysela, Takashi Iwai, alsa-devel, linux-kernel,
Matthias Kaehlcke, Jeffy Chen, linux-rockchip
In-Reply-To: <20171219105818.GH8563@sirena.org.uk>
On Tue, Dec 19, 2017 at 10:58:18AM +0000, Mark Brown wrote:
> It's been applied as a fix for some time.
Indeed it has. Sorry for missing that. I look forward to seeing it in a
release candidate, so my system will again work on mainline :)
Brian
^ permalink raw reply
* Re: [PATCH v4] usb: dwc2: host: Don't retry NAKed transactions right away
From: Doug Anderson @ 2017-12-19 17:19 UTC (permalink / raw)
To: Stefan Wahren
Cc: John Youn, Felipe Balbi, Matthias Kaehlcke,
open list:ARM/Rockchip SoC..., Julius Werner, Eric Anholt,
Greg Kroah-Hartman, LKML, John Stultz, linux-usb, Johan Hovold,
Alexandru M Stan, Alyssa Rosenzweig, linux-rpi-kernel
In-Reply-To: <581796864.256888.1513702600488@email.1und1.de>
Hi,
On Tue, Dec 19, 2017 at 8:56 AM, Stefan Wahren <stefan.wahren@i2se.com> wrote:
> Hi Doug,
>
>> Doug Anderson <dianders@chromium.org> hat am 19. Dezember 2017 um 16:57 geschrieben:
>>
>>
>> Felipe,
>>
>> On Tue, Dec 12, 2017 at 10:30 AM, Douglas Anderson
>> <dianders@chromium.org> wrote:
>> ...
>>
>> I don't mean to be a pest, but I'm hoping that we can land this
>> somewhere (if nothing else in your /next tree) just so it doesn't miss
>> another release cycle. If you're not so keen on collecting dwc2 host
>> patches these days, I can also see if Greg KH is willing to take this
>> directly into his tree. Please let me know. Thanks for your time!
>> :)
>>
>> -Doug
>
> it's applied to next:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git/commit/?h=next&id=38d2b5fb75c15923fb89c32134516a623515bce4
Awww, crud. OK, then _really_ I'm just being a pest. :( Thanks for
pointing it out Stefan. I'm so used to all the emails that fly by
from folks when things land that I didn't think to check directly.
Sorry for the noise Felipe and thanks to both of you guys your all
your help.
-Doug
^ permalink raw reply
* Re: [PATCH v4] usb: dwc2: host: Don't retry NAKed transactions right away
From: Stefan Wahren @ 2017-12-19 16:56 UTC (permalink / raw)
To: John Youn, Doug Anderson, Felipe Balbi
Cc: Alexandru M Stan, open list:ARM/Rockchip SoC...,
Greg Kroah-Hartman, linux-usb-u79uwXL29TY76Z2rM5mHXA, LKML,
Johan Hovold, Eric Anholt, Matthias Kaehlcke, John Stultz,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Julius Werner,
Alyssa Rosenzweig
In-Reply-To: <CAD=FV=WksZcVY1fu-KX9E-cPvBEA6LaOMrAoAgh=GDWDtjCWFw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Doug,
> Doug Anderson <dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> hat am 19. Dezember 2017 um 16:57 geschrieben:
>
>
> Felipe,
>
> On Tue, Dec 12, 2017 at 10:30 AM, Douglas Anderson
> <dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> wrote:
> ...
>
> I don't mean to be a pest, but I'm hoping that we can land this
> somewhere (if nothing else in your /next tree) just so it doesn't miss
> another release cycle. If you're not so keen on collecting dwc2 host
> patches these days, I can also see if Greg KH is willing to take this
> directly into his tree. Please let me know. Thanks for your time!
> :)
>
> -Doug
it's applied to next:
https://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git/commit/?h=next&id=38d2b5fb75c15923fb89c32134516a623515bce4
^ permalink raw reply
* Re: [PATCH v4] usb: dwc2: host: Don't retry NAKed transactions right away
From: Doug Anderson @ 2017-12-19 15:57 UTC (permalink / raw)
To: Felipe Balbi, John Youn
Cc: Stefan Wahren, Alexandru M Stan, open list:ARM/Rockchip SoC...,
Greg Kroah-Hartman, Johan Hovold, Eric Anholt, Matthias Kaehlcke,
John Stultz, linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Julius Werner, Alyssa Rosenzweig, Douglas Anderson,
linux-usb-u79uwXL29TY76Z2rM5mHXA, LKML
In-Reply-To: <20171212183031.46772-1-dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Felipe,
On Tue, Dec 12, 2017 at 10:30 AM, Douglas Anderson
<dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> wrote:
> On rk3288-veyron devices on Chrome OS it was found that plugging in an
> Arduino-based USB device could cause the system to lockup, especially
> if the CPU Frequency was at one of the slower operating points (like
> 100 MHz / 200 MHz).
>
> Upon tracing, I found that the following was happening:
> * The USB device (full speed) was connected to a high speed hub and
> then to the rk3288. Thus, we were dealing with split transactions,
> which is all handled in software on dwc2.
> * Userspace was initiating a BULK IN transfer
> * When we sent the SSPLIT (to start the split transaction), we got an
> ACK. Good. Then we issued the CSPLIT.
> * When we sent the CSPLIT, we got back a NAK. We immediately (from
> the interrupt handler) started to retry and sent another SSPLIT.
> * The device kept NAKing our CSPLIT, so we kept ping-ponging between
> sending a SSPLIT and a CSPLIT, each time sending from the interrupt
> handler.
> * The handling of the interrupts was (because of the low CPU speed and
> the inefficiency of the dwc2 interrupt handler) was actually taking
> _longer_ than it took the other side to send the ACK/NAK. Thus we
> were _always_ in the USB interrupt routine.
> * The fact that USB interrupts were always going off was preventing
> other things from happening in the system. This included preventing
> the system from being able to transition to a higher CPU frequency.
>
> As I understand it, there is no requirement to retry super quickly
> after a NAK, we just have to retry sometime in the future. Thus one
> solution to the above is to just add a delay between getting a NAK and
> retrying the transmission. If this delay is sufficiently long to get
> out of the interrupt routine then the rest of the system will be able
> to make forward progress. Even a 25 us delay would probably be
> enough, but we'll be extra conservative and try to delay 1 ms (the
> exact amount depends on HZ and the accuracy of the jiffy and how close
> the current jiffy is to ticking, but could be as much as 20 ms or as
> little as 1 ms).
>
> Presumably adding a delay like this could impact the USB throughput,
> so we only add the delay with repeated NAKs.
>
> NOTE: Upon further testing of a pl2303 serial adapter, I found that
> this fix may help with problems there. Specifically I found that the
> pl2303 serial adapters tend to respond with a NAK when they have
> nothing to say and thus we end with this same sequence.
>
> Signed-off-by: Douglas Anderson <dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> Reviewed-by: Julius Werner <jwerner-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> Tested-by: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>
> Acked-by: John Youn <johnyoun-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
> ---
>
> Changes in v4:
> - Removed Cc for stable as per Felipe's request in v3
> - Rebased and squashed the two patches since Kees' timer stuff landed
> - Add John Youn's Ack.
>
> Changes in v3:
> - Add tested-by for Stefan Wahren
> - Sent to Felipe Balbi as candiate to land this.
> - Add Cc for stable (it's always been broken so go as far is as easy)
>
> Changes in v2:
> - Address http://crosreview.com/737520 feedback
>
> drivers/usb/dwc2/core.h | 1 +
> drivers/usb/dwc2/hcd.c | 7 ++++
> drivers/usb/dwc2/hcd.h | 9 +++++
> drivers/usb/dwc2/hcd_intr.c | 20 +++++++++++
> drivers/usb/dwc2/hcd_queue.c | 81 +++++++++++++++++++++++++++++++++++++++++---
> 5 files changed, 114 insertions(+), 4 deletions(-)
I don't mean to be a pest, but I'm hoping that we can land this
somewhere (if nothing else in your /next tree) just so it doesn't miss
another release cycle. If you're not so keen on collecting dwc2 host
patches these days, I can also see if Greg KH is willing to take this
directly into his tree. Please let me know. Thanks for your time!
:)
-Doug
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox