From edbfad5e8605a8d36334fd6637e72e0f93628850 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Mon, 22 Jan 2024 09:50:14 +0000 Subject: [PATCH] media: i2c: rk628: add suspend and resume support Change-Id: I8da6815fe9f95d7e29e7dd0fad186e2dbbc35b42 Signed-off-by: Jianwei Fan --- drivers/media/i2c/rk628/rk628_bt1120_v4l2.c | 132 ++++++++++++++---- drivers/media/i2c/rk628/rk628_csi_v4l2.c | 141 +++++++++++++++----- 2 files changed, 214 insertions(+), 59 deletions(-) diff --git a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c index bfb1653c80d8..0a713ae6d37d 100644 --- a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c @@ -820,13 +820,12 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) return 0; } - -static void rk628_bt1120_initial_setup(struct v4l2_subdev *sd) +static void rk628_bt1120_initial(struct v4l2_subdev *sd) { struct rk628_bt1120 *bt1120 = to_bt1120(sd); struct v4l2_subdev_edid def_edid; - /* selete int io function */ + /* select int io function */ rk628_i2c_write(bt1120->rk628, GRF_GPIO3AB_SEL_CON, 0x30002000); rk628_i2c_write(bt1120->rk628, GRF_GPIO1AB_SEL_CON, HIWORD_UPDATE(0x7, 11, 8)); /* I2S_SCKM0 */ @@ -873,7 +872,13 @@ static void rk628_bt1120_initial_setup(struct v4l2_subdev *sd) def_edid.edid = edid_init_data; rk628_bt1120_s_edid(sd, &def_edid); rk628_hdmirx_set_hdcp(bt1120->rk628, &bt1120->hdcp, false); +} +static void rk628_bt1120_initial_setup(struct v4l2_subdev *sd) +{ + struct rk628_bt1120 *bt1120 = to_bt1120(sd); + + rk628_bt1120_initial(sd); if (tx_5v_power_present(sd)) schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, 4000); } @@ -1685,6 +1690,91 @@ static irqreturn_t plugin_detect_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static int rk628_bt1120_power_on(struct rk628_bt1120 *bt1120) +{ + clk_prepare_enable(bt1120->soc_24M); + if (bt1120->enable_gpio) { + gpiod_set_value(bt1120->enable_gpio, 1); + usleep_range(10000, 11000); + } + gpiod_set_value(bt1120->reset_gpio, 0); + usleep_range(10000, 11000); + gpiod_set_value(bt1120->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value(bt1120->reset_gpio, 0); + usleep_range(10000, 11000); + + if (bt1120->power_gpio) { + gpiod_set_value(bt1120->power_gpio, 1); + usleep_range(10000, 11000); + } + + return 0; +} + +static int rk628_bt1120_power_off(struct rk628_bt1120 *bt1120) +{ + if (bt1120->enable_gpio) { + gpiod_set_value(bt1120->enable_gpio, 0); + usleep_range(10000, 11000); + } + gpiod_set_value(bt1120->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value(bt1120->reset_gpio, 0); + usleep_range(10000, 11000); + gpiod_set_value(bt1120->reset_gpio, 1); + usleep_range(10000, 11000); + + if (bt1120->power_gpio) { + gpiod_set_value(bt1120->power_gpio, 0); + usleep_range(10000, 11000); + } + clk_disable_unprepare(bt1120->soc_24M); + + return 0; +} + +static int rk628_bt1120_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct rk628_bt1120 *bt1120 = to_bt1120(sd); + + v4l2_info(sd, "%s: resume!\n", __func__); + + rk628_bt1120_power_on(bt1120); + rk628_cru_initialize(bt1120->rk628); + rk628_bt1120_initial(sd); + rk628_hdmirx_plugout(sd); + enable_irq(bt1120->hdmirx_irq); + schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, + msecs_to_jiffies(500)); + + return 0; +} + +static int rk628_bt1120_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct rk628_bt1120 *bt1120 = to_bt1120(sd); + + v4l2_info(sd, "%s: suspend!\n", __func__); + + disable_irq(bt1120->hdmirx_irq); + cancel_delayed_work_sync(&bt1120->delayed_work_res_change); + cancel_delayed_work_sync(&bt1120->delayed_work_enable_hotplug); + rk628_hdmirx_audio_cancel_work_audio(bt1120->audio_info, true); + rk628_bt1120_power_off(bt1120); + + return 0; +} + +static const struct dev_pm_ops rk628_bt1120_pm_ops = { + .suspend = rk628_bt1120_suspend, + .resume = rk628_bt1120_resume, +}; + static int rk628_bt1120_probe_of(struct rk628_bt1120 *bt1120) { struct device *dev = bt1120->dev; @@ -1701,28 +1791,27 @@ static int rk628_bt1120_probe_of(struct rk628_bt1120 *bt1120) ret = PTR_ERR(bt1120->soc_24M); dev_err(dev, "Unable to get soc_24M: %d\n", ret); } - clk_prepare_enable(bt1120->soc_24M); bt1120->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(bt1120->enable_gpio)) { ret = PTR_ERR(bt1120->enable_gpio); dev_err(dev, "failed to request enable GPIO: %d\n", ret); - goto clk_put; + return ret; } bt1120->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(bt1120->reset_gpio)) { ret = PTR_ERR(bt1120->reset_gpio); dev_err(dev, "failed to request reset GPIO: %d\n", ret); - goto clk_put; + return ret; } bt1120->power_gpio = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_HIGH); if (IS_ERR(bt1120->power_gpio)) { dev_err(dev, "failed to get power gpio\n"); ret = PTR_ERR(bt1120->power_gpio); - goto clk_put; + return ret; } bt1120->plugin_det_gpio = devm_gpiod_get_optional(dev, "plugin-det", @@ -1730,26 +1819,10 @@ static int rk628_bt1120_probe_of(struct rk628_bt1120 *bt1120) if (IS_ERR(bt1120->plugin_det_gpio)) { dev_err(dev, "failed to get hdmirx det gpio\n"); ret = PTR_ERR(bt1120->plugin_det_gpio); - goto clk_put; + return ret; } bt1120->rk628->hdmirx_det_gpio = bt1120->plugin_det_gpio; - if (bt1120->enable_gpio) { - gpiod_set_value(bt1120->enable_gpio, 1); - usleep_range(10000, 11000); - } - gpiod_set_value(bt1120->reset_gpio, 0); - usleep_range(10000, 11000); - gpiod_set_value(bt1120->reset_gpio, 1); - usleep_range(10000, 11000); - gpiod_set_value(bt1120->reset_gpio, 0); - usleep_range(10000, 11000); - - if (bt1120->power_gpio) { - gpiod_set_value(bt1120->power_gpio, 1); - usleep_range(500, 510); - } - if (of_property_read_bool(dev->of_node, "hdcp-enable")) hdcp1x_enable = true; @@ -1769,14 +1842,14 @@ static int rk628_bt1120_probe_of(struct rk628_bt1120 *bt1120) if (!ep) { dev_err(dev, "missing endpoint node\n"); ret = -EINVAL; - goto clk_put; + return ret; } ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); of_node_put(ep); if (ret) { dev_err(dev, "failed to parse endpoint\n"); - goto clk_put; + return ret; } bt1120->enable_hdcp = hdcp1x_enable; @@ -1793,9 +1866,6 @@ static int rk628_bt1120_probe_of(struct rk628_bt1120 *bt1120) v4l2_fwnode_endpoint_free(&endpoint); -clk_put: - clk_disable_unprepare(bt1120->soc_24M); - return ret; } @@ -1855,6 +1925,7 @@ static int rk628_bt1120_probe(struct i2c_client *client, return err; } + rk628_bt1120_power_on(bt1120); rk628_cru_initialize(rk628); rk628_version_parse(rk628); @@ -2007,6 +2078,7 @@ err_hdl: mutex_destroy(&bt1120->confctl_mutex); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(&bt1120->hdl); + rk628_bt1120_power_off(bt1120); return err; } @@ -2039,6 +2111,7 @@ static int rk628_bt1120_remove(struct i2c_client *client) rk628_control_assert(bt1120->rk628, RGU_CLK_RX); rk628_control_assert(bt1120->rk628, RGU_VOP); rk628_control_assert(bt1120->rk628, RGU_BT1120DEC); + rk628_bt1120_power_off(bt1120); return 0; } @@ -2059,6 +2132,7 @@ MODULE_DEVICE_TABLE(of, rk628_bt1120_of_match); static struct i2c_driver rk628_bt1120_i2c_driver = { .driver = { .name = "rk628-bt1120-v4l2", + .pm = &rk628_bt1120_pm_ops, .of_match_table = of_match_ptr(rk628_bt1120_of_match), }, .id_table = rk628_bt1120_i2c_id, diff --git a/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/drivers/media/i2c/rk628/rk628_csi_v4l2.c index bf0a32710aee..82d7d064266f 100644 --- a/drivers/media/i2c/rk628/rk628_csi_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_csi_v4l2.c @@ -1277,14 +1277,14 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) return 0; } -static void rk628_csi_initial_setup(struct v4l2_subdev *sd) +static void rk628_csi_initial(struct v4l2_subdev *sd) { struct rk628_csi *csi = to_csi(sd); struct v4l2_subdev_edid def_edid; u32 mask = SW_OUTPUT_MODE_MASK; u32 val = SW_OUTPUT_MODE(OUTPUT_MODE_CSI); - /* selete int io function */ + /* select int io function */ rk628_i2c_write(csi->rk628, GRF_GPIO3AB_SEL_CON, 0x30002000); rk628_i2c_write(csi->rk628, GRF_GPIO1AB_SEL_CON, HIWORD_UPDATE(0xf, 11, 8)); /* I2S_SCKM0 */ @@ -1341,7 +1341,13 @@ static void rk628_csi_initial_setup(struct v4l2_subdev *sd) def_edid.edid = edid_init_data; rk628_csi_s_edid(sd, &def_edid); rk628_hdmirx_set_hdcp(csi->rk628, &csi->hdcp, false); +} +static void rk628_csi_initial_setup(struct v4l2_subdev *sd) +{ + struct rk628_csi *csi = to_csi(sd); + + rk628_csi_initial(sd); if (csi->rk628->version >= RK628F_VERSION) { csi->rk628->mipi_timing[0] = rk628f_csi0_mipi; csi->rk628->mipi_timing[1] = rk628f_csi1_mipi; @@ -2644,6 +2650,91 @@ static irqreturn_t plugin_detect_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static int rk628_csi_power_on(struct rk628_csi *csi) +{ + clk_prepare_enable(csi->soc_24M); + if (csi->enable_gpio) { + gpiod_set_value(csi->enable_gpio, 1); + usleep_range(10000, 11000); + } + gpiod_set_value(csi->reset_gpio, 0); + usleep_range(10000, 11000); + gpiod_set_value(csi->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value(csi->reset_gpio, 0); + usleep_range(10000, 11000); + + if (csi->power_gpio) { + gpiod_set_value(csi->power_gpio, 1); + usleep_range(10000, 11000); + } + + return 0; +} + +static int rk628_csi_power_off(struct rk628_csi *csi) +{ + if (csi->enable_gpio) { + gpiod_set_value(csi->enable_gpio, 0); + usleep_range(10000, 11000); + } + gpiod_set_value(csi->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value(csi->reset_gpio, 0); + usleep_range(10000, 11000); + gpiod_set_value(csi->reset_gpio, 1); + usleep_range(10000, 11000); + + if (csi->power_gpio) { + gpiod_set_value(csi->power_gpio, 0); + usleep_range(10000, 11000); + } + clk_disable_unprepare(csi->soc_24M); + + return 0; +} + +static int rk628_csi_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct rk628_csi *csi = to_csi(sd); + + v4l2_info(sd, "%s: resume!\n", __func__); + + rk628_csi_power_on(csi); + rk628_cru_initialize(csi->rk628); + rk628_csi_initial(sd); + rk628_hdmirx_plugout(sd); + enable_irq(csi->hdmirx_irq); + schedule_delayed_work(&csi->delayed_work_enable_hotplug, + msecs_to_jiffies(500)); + + return 0; +} + +static int rk628_csi_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct rk628_csi *csi = to_csi(sd); + + v4l2_info(sd, "%s: suspend!\n", __func__); + + disable_irq(csi->hdmirx_irq); + cancel_delayed_work_sync(&csi->delayed_work_res_change); + cancel_delayed_work_sync(&csi->delayed_work_enable_hotplug); + rk628_hdmirx_audio_cancel_work_audio(csi->audio_info, true); + rk628_csi_power_off(csi); + + return 0; +} + +static const struct dev_pm_ops rk628_csi_pm_ops = { + .suspend = rk628_csi_suspend, + .resume = rk628_csi_resume, +}; + static int rk628_csi_probe_of(struct rk628_csi *csi) { struct device *dev = csi->dev; @@ -2661,28 +2752,27 @@ static int rk628_csi_probe_of(struct rk628_csi *csi) ret = PTR_ERR(csi->soc_24M); dev_err(dev, "Unable to get soc_24M: %d\n", ret); } - clk_prepare_enable(csi->soc_24M); csi->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(csi->enable_gpio)) { ret = PTR_ERR(csi->enable_gpio); dev_err(dev, "failed to request enable GPIO: %d\n", ret); - goto clk_put; + return ret; } csi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(csi->reset_gpio)) { ret = PTR_ERR(csi->reset_gpio); dev_err(dev, "failed to request reset GPIO: %d\n", ret); - goto clk_put; + return ret; } csi->power_gpio = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_HIGH); if (IS_ERR(csi->power_gpio)) { dev_err(dev, "failed to get power gpio\n"); ret = PTR_ERR(csi->power_gpio); - goto clk_put; + return ret; } csi->plugin_det_gpio = devm_gpiod_get_optional(dev, "plugin-det", @@ -2690,26 +2780,10 @@ static int rk628_csi_probe_of(struct rk628_csi *csi) if (IS_ERR(csi->plugin_det_gpio)) { dev_err(dev, "failed to get hdmirx det gpio\n"); ret = PTR_ERR(csi->plugin_det_gpio); - goto clk_put; + return ret; } csi->rk628->hdmirx_det_gpio = csi->plugin_det_gpio; - if (csi->enable_gpio) { - gpiod_set_value(csi->enable_gpio, 1); - usleep_range(10000, 11000); - } - gpiod_set_value(csi->reset_gpio, 0); - usleep_range(10000, 11000); - gpiod_set_value(csi->reset_gpio, 1); - usleep_range(10000, 11000); - gpiod_set_value(csi->reset_gpio, 0); - usleep_range(10000, 11000); - - if (csi->power_gpio) { - gpiod_set_value(csi->power_gpio, 1); - usleep_range(500, 510); - } - if (of_property_read_bool(dev->of_node, "hdcp-enable")) hdcp1x_enable = true; @@ -2736,7 +2810,7 @@ static int rk628_csi_probe_of(struct rk628_csi *csi) if (!ep) { dev_err(dev, "missing endpoint node\n"); ret = -EINVAL; - goto clk_put; + return ret; } ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); @@ -2771,8 +2845,6 @@ free_endpoint: v4l2_fwnode_endpoint_free(&endpoint); put_node: of_node_put(ep); -clk_put: - clk_disable_unprepare(csi->soc_24M); return ret; } @@ -2929,6 +3001,7 @@ static int rk628_csi_probe(struct i2c_client *client, return err; } + rk628_csi_power_on(csi); rk628_cru_initialize(csi->rk628); rk628_version_parse(rk628); @@ -2939,14 +3012,15 @@ static int rk628_csi_probe(struct i2c_client *client, v4l2_info(sd, "get multi dev info failed, not use dual mipi mode\n"); } - v4l2_subdev_init(sd, &rk628_csi_ops); + v4l2_i2c_subdev_init(sd, client, &rk628_csi_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; /* i2c access, read chip id*/ err = rk628_i2c_read(csi->rk628, CSITX_CSITX_VERSION, &val); if (err) { v4l2_err(sd, "i2c access failed! err:%d\n", err); - return -ENODEV; + err = -ENODEV; + goto power_off; } v4l2_dbg(1, debug, sd, "CSITX VERSION: %#x\n", val); @@ -2954,7 +3028,8 @@ static int rk628_csi_probe(struct i2c_client *client, err = rk628_i2c_read(csi->rk628, CSITX1_CSITX_VERSION, &val); if (err) { v4l2_err(sd, "i2c access failed! err:%d\n", err); - return -ENODEV; + err = -ENODEV; + goto power_off; } v4l2_dbg(1, debug, sd, "CSITX1 VERSION: %#x\n", val); } @@ -2964,7 +3039,8 @@ static int rk628_csi_probe(struct i2c_client *client, csi->txphy = rk628_txphy_register(rk628); if (!csi->txphy) { v4l2_err(sd, "register txphy failed\n"); - return -ENOMEM; + err = -ENOMEM; + goto power_off; } /* control handlers */ @@ -3112,6 +3188,9 @@ err_hdl: mutex_destroy(&csi->confctl_mutex); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(&csi->hdl); +power_off: + rk628_csi_power_off(csi); + return err; } @@ -3153,6 +3232,7 @@ static int rk628_csi_remove(struct i2c_client *client) rk628_control_assert(csi->rk628, RGU_CSI); if (csi->rk628->version >= RK628F_VERSION) rk628_control_assert(csi->rk628, RGU_CSI1); + rk628_csi_power_off(csi); #if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE return 0; #endif @@ -3161,6 +3241,7 @@ static int rk628_csi_remove(struct i2c_client *client) static struct i2c_driver rk628_csi_i2c_driver = { .driver = { .name = "rk628-csi-v4l2", + .pm = &rk628_csi_pm_ops, .of_match_table = of_match_ptr(rk628_csi_of_match), }, .id_table = rk628_csi_i2c_id,