|
|
|
|
|
|
soc平台 |
|
开发平台 |
||||
|
|
ubuntu18.04 64位 |
学嵌入式开发也有一段时间了,本来也是想搞一款音视频编码器产品,出于这个目的,也验证一下自己的能力。
由于现在开发板是现成的了,引出了对应MIPI CSI的两组总线,需要自己做个HDMI转MIPI的模块,自己根据芯片手册,简单设计了一个LT6911UXC的转换模块(调试板有点脏乱)。
整个硬件平台算是搭建完成了。
这里说是移植,而不是开发的原因是,正好手头上有已经比较完美的驱动了。只是硬件平台的改动而已。
这里设计的是挂载在I2C2上的,所以
&i2c2{ status = "okay"; clock-frequency =; lt6911uxc: lt6911uxc@2b { compatible = "LT6911UXC"; reg =; }; };
都知道,设备树是描述硬件资源的。既然硬件平台改了,那么对于新的硬件平台当然是要在设备树中加入设备和相关信息了
源码中有这样一个函数
#ifdef CONFIG_OF
static int lt6911uxc_parse_of(struct lt6911uxc *lt6911uxc)
{
struct device *dev = <6911uxc->i2c_client->dev;
struct device_node *node = dev->of_node;
struct v4l2_fwnode_endpoint *endpoint;
struct device_node *ep;
int ret;
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
<6911uxc->module_index);
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
<6911uxc->module_facing);
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
<6911uxc->module_name);
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
<6911uxc->len_name);
if (ret) {
dev_err(dev, "could not get module information!\n");
return -EINVAL;
}
#if 0
lt6911uxc->power_gpio = devm_gpiod_get_optional(dev, "power", //电源控制
GPIOD_OUT_LOW);
if (IS_ERR(lt6911uxc->power_gpio)) {
dev_err(dev, "failed to get power gpio\n");
ret = PTR_ERR(lt6911uxc->power_gpio);
return ret;
}
#endif
lt6911uxc->reset_gpio = devm_gpiod_get_optional(dev, "reset", //复位引脚
GPIOD_OUT_LOW);
if (IS_ERR(lt6911uxc->reset_gpio)) {
dev_err(dev, "failed to get reset gpio\n");
ret = PTR_ERR(lt6911uxc->reset_gpio);
return ret;
}
lt6911uxc->plugin_det_gpio = devm_gpiod_get_optional(dev, "plugin-det", //插入检测
GPIOD_IN);
if (IS_ERR(lt6911uxc->plugin_det_gpio)) {
dev_err(dev, "failed to get plugin det gpio\n");
ret = PTR_ERR(lt6911uxc->plugin_det_gpio);
return ret;
}
#if 0
lt6911uxc->hpd_ctl_gpio = devm_gpiod_get_optional(dev, "hpd-ctl", //热插拔控制
GPIOD_OUT_HIGH);
if (IS_ERR(lt6911uxc->hpd_ctl_gpio)) {
dev_err(dev, "failed to get hpd ctl gpio\n");
ret = PTR_ERR(lt6911uxc->hpd_ctl_gpio);
return ret;
}
#endif
//获取和配置v4l2相关端点
ep = of_graph_get_next_endpoint(dev->of_node, NULL);
if (!ep) {
dev_err(dev, "missing endpoint node\n");
ret = -EINVAL;
return ret;
}
endpoint = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep));
if (IS_ERR(endpoint)) {
dev_err(dev, "failed to parse endpoint\n");
ret = PTR_ERR(endpoint);
return ret;
}
if (endpoint->bus_type != V4L2_MBUS_CSI2 ||
endpoint->bus.mipi_csi2.num_data_lanes == 0) {
dev_err(dev, "missing CSI-2 properties in endpoint\n");
ret = -EINVAL;
goto free_endpoint;
}
#if 1
lt6911uxc->xvclk = devm_clk_get(dev, "xvclk"); //如果lt6911的外部时钟是soc提供,则需要进行相关配置
if (IS_ERR(lt6911uxc->xvclk)) {
dev_err(dev, "failed to get xvclk\n");
ret = -EINVAL;
goto free_endpoint;
}
ret = clk_prepare_enable(lt6911uxc->xvclk);
if (ret) {
dev_err(dev, "Failed! to enable xvclk\n");
goto free_endpoint;
}
#endif
lt6911uxc->csi_lanes_in_use = endpoint->bus.mipi_csi2.num_data_lanes;
lt6911uxc->bus = endpoint->bus.mipi_csi2;
lt6911uxc->enable_hdcp = false;
// gpiod_set_value(lt6911uxc->hpd_ctl_gpio, 0);
// gpiod_set_value(lt6911uxc->power_gpio, 1);
lt6911uxc_reset(lt6911uxc);
ret = 0;
free_endpoint:
v4l2_fwnode_endpoint_free(endpoint);
return ret;
}
从整个处理过程中,此函数是在通过devm_gpiod_get_optional这个函数查找设备树中 "power-gpios" , "reset-gpios", "plugin-det-gpios", "hpd-ctl-gpios"相关节点,并进行输入输出模式配置。将返回的结果存储到定义的lt6911uxc私有数据中。
根据以上源码,对设备树进行如下配置(根据电路设计,power和HPD的控制这里没有用到,所以这里不做配置)
reset-gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_LOW>;
plugin-det-gpios = <&gpio2 RK_PB0 IRQ_TYPE_LEVEL_LOW>;
//hpd-ctl-gpios = <&gpio2 RK_PD4 GPIO_ACTIVE_LOW>;
复位函数
static void lt6911uxc_reset(struct lt6911uxc *lt6911uxc)
{
gpiod_set_value(lt6911uxc->reset_gpio, 0);
usleep_range(2000, 2100);
gpiod_set_value(lt6911uxc->reset_gpio, 1);
usleep_range(120*1000, 121*1000);
gpiod_set_value(lt6911uxc->reset_gpio, 0);
usleep_range(300*1000, 310*1000);
}
插入检测函数
static inline bool tx_5v_power_present(struct v4l2_subdev *sd)
{
int val;
struct lt6911uxc *lt6911uxc = to_state(sd);
val = gpiod_get_value(lt6911uxc->plugin_det_gpio);
v4l2_dbg(1, debug, sd, "%s plug det: %s!\n", __func__,
(val > 0) ? "int" : "out");
return (val > 0);
}
static irqreturn_t plugin_detect_irq_handler(int irq, void *dev_id)
{
struct lt6911uxc *lt6911uxc = dev_id;
struct v4l2_subdev *sd = <6911uxc->sd;
/* control hpd output level after 25ms */
// schedule_delayed_work(delayed_work_enable_hotplug,
// HZ / 40);
tx_5v_power_present(sd);
return IRQ_HANDLED;
}
&i2c2{
status = "okay";
clock-frequency = <100000>;
lt6911uxc: lt6911uxc@2b {
compatible = "LT6911UXC";
reg = <0x2b>;
clocks = <&cru CLK_MIPICSI_OUT>;
clock-names = "xvclk";
interrupt-parent=<&gpio3>;
interrupts=<RK_PA5 IRQ_TYPE_LEVEL_LOW>;
pinctrl-names = "default";
pinctrl-0 = <<6911uxc_pin>;
reset-gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_LOW>;
plugin-det-gpios = <&gpio2 RK_PB0 IRQ_TYPE_LEVEL_LOW>;
// hpd-ctl-gpios = <&gpio2 RK_PD4 GPIO_ACTIVE_LOW>;
rockchip,camera-module-index = <0>;
rockchip,camera-module-facing = "back";
rockchip,camera-module-name = "hdmi_in"; //摄像头名称
rockchip,camera-module-lens-name = "lt6911uxc"; //摄像头镜头
port { //v4l2端点配置
ucam_out0: endpoint {
remote-endpoint = <&mipi_in_ucam0>;
data-lanes = <1 2 3 4>;
};
};
};
};
&csi_dphy0 {
status = "okay";
ports {
#address-cells =;
#size-cells =;
port@0 {
reg = <0>;
#address-cells =;
#size-cells =;
/*mipi_in_ucam0: endpoint@1 {
reg =;
remote-endpoint = <&ucam_out0>;
data-lanes =;
};*/
mipi_in_ucam0: endpoint@1 {
reg = <1>;
remote-endpoint = <&ucam_out0>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
reg = <1>;
#address-cells =;
#size-cells =;
csidphy0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi_csi2_input>;
data-lanes = <1 2 3 4>;
};
};
};
};
&mipi_csi2 {
status = "okay";
ports {
#address-cells =;
#size-cells =;
port@0 {
reg = <0>;
#address-cells =;
#size-cells =;
mipi_csi2_input: endpoint@1 {
reg = <1>;
remote-endpoint = <&csidphy0_out>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
reg = <1>;
#address-cells =;
#size-cells =;
mipi_csi2_output: endpoint@0 {
reg = <0>;
remote-endpoint = <&cif_mipi_in>;
data-lanes = <1 2 3 4>;
};
};
};
};
以上也就配置完成了,具体调试过程可以参看我的另一篇文章
RK628底层调试,使用V4L2调试工具抓图-面包板社区 (eet-china.com)
rtsp获取demo可以使用瑞芯微SDK自带的
SDK/external/rkmedia/examples/rkmedia_vi_venc_rtsp_test.c