summaryrefslogtreecommitdiff
path: root/drivers
AgeCommit message (Collapse)Author
2016-07-30drm/nouveau/acpi: ensure matching ACPI handle and supported functionsPeter Wu
Ensure that the returned set of supported DSM functions (MUX, Optimus) match the ACPI handle that is set in nouveau_dsm_pci_probe. As there are no machines with a MUX function on just one PCI device and an Optimus on another, there should not be a functional impact. This change however makes this implicit assumption more obvious. Convert int to bool and rename has_dsm to has_mux while at it. Let the caller set nouveau_dsm_priv.dhandle as needed. v2: pass dhandle to the caller. Reviewed-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Peter Wu <peter@lekensteyn.nl> Acked-by: Dave Airlie <airlied@redhat.com Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
2016-07-30drm/nouveau/fbcon: fix font width not divisible by 8Mikulas Patocka
The patch f045f459d925 ("drm/nouveau/fbcon: fix out-of-bounds memory accesses") tries to fix some out of memory accesses. Unfortunatelly, the patch breaks the display when using fonts with width that is not divisiable by 8. The monochrome bitmap for each character is stored in memory by lines from top to bottom. Each line is padded to a full byte. For example, for 22x11 font, each line is padded to 16 bits, so each character is consuming 44 bytes total, that is 11 32-bit words. The patch f045f459d925 changed the logic to "dsize = ALIGN(image->width * image->height, 32) >> 5", that is just 8 words - this is incorrect and it causes display corruption. This patch adds the necesary padding of lines to 8 bytes. This patch should be backported to stable kernels where f045f459d925 was backported. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Fixes: f045f459d925 ("drm/nouveau/fbcon: fix out-of-bounds memory accesses") Cc: stable@vger.kernel.org Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
2016-07-22Merge branch 'for-next' of http://git.agner.ch/git/linux-drm-fsl-dcu into ↵Dave Airlie
drm-next This adds drm bridge support for the NXP/Freescale DCU. The patchset has been discussed on the mailing list since quite some time... Plus there is a small fix provided by Peter. * 'for-next' of http://git.agner.ch/git/linux-drm-fsl-dcu: drm/fsl-dcu: add support for drm bridge drm/fsl-dcu: rework codes to support of_graph dt binding for panel drm/fsl-dcu: add missing of_node_put after calling of_parse_phandle
2016-07-22Merge branch 'drm-rockchip-next-fixes-2016-07-19' of ↵Dave Airlie
https://github.com/markyzq/kernel-drm-rockchip into drm-next Here are some little fixes for rockchip drm, looks good for me, and there is no doubt on them, So I'd like you can land them. * 'drm-rockchip-next-fixes-2016-07-19' of https://github.com/markyzq/kernel-drm-rockchip: drm/rockchip: allocate correct crtc state structure on reset drm/rockchip: Delete an unnecessary check before drm_gem_object_unreference_unlocked() drm/rockchip: fix a couple off by one bugs drm/rockchip: vop: correct rk3036 register define drm/rockchip: vop: correct the source size of uv scale factor setting drm/rockchip: vop: add uv_vir register field for RK3036 VOP drm/rockchip: fix "should it be static?" warnings drm/rockchip: fb: add missing header drm/rockchip: dw_hdmi: remove unused #include
2016-07-19Merge branch 'drm_bridge_for_4.8' of https://github.com/boddob/linux into ↵Dave Airlie
drm-next This is an update to the previous drm bridge pull request. The ADV7511 driver's conversion from slave encoder to bridge meant that its users (the rcar-du kms driver) should use the bridge interface too. This pull request now also contains a commit that updates the rcar-du's hdmi encoder interface from slave encoder to bridge. The other updates are as before: - Converts the ADV7511 i2c slave encoder driver to a bridge driver. Adds support for the ADV7533 bridge chip. - Add bridge driver for TC358767 (DSI/DPI to eDP) encoder chips. * 'drm_bridge_for_4.8' of https://github.com/boddob/linux: drm: rcar-du: Remove i2c slave encoder interface for hdmi encoder drm/bridge: tc358767: Add DPI to eDP bridge driver dt-bindings: tc358767: add DT documentation dt-bindings: drm/bridge: Update bindings for ADV7533 drm/bridge: adv7533: Change number of DSI lanes dynamically drm/bridge: adv7533: Use internal timing generator drm/bridge: adv7533: Create a MIPI DSI device drm/bridge: adv7533: Initial support for ADV7533 drm/bridge: adv7511: Fix mutex deadlock when interrupts are disabled drm/i2c: adv7511: Move to bridge folder drm/i2c: adv7511: Convert to drm_bridge
2016-07-19drm/rockchip: allocate correct crtc state structure on resetJohn Keeping
Because we are using a custom crtc_state structure, we must override the reset helper to allocate the correct amount of memory. Cc: stable@vger.kernel.org Fixes: 4e257d9eee23 ("drm/rockchip: get rid of rockchip_drm_crtc_mode_config") Signed-off-by: John Keeping <john@metanate.com> Signed-off-by: Mark Yao <mark.yao@rock-chips.com> Reviewed-by: Sean Paul <seanpaul@chromium.org>
2016-07-19drm/rockchip: Delete an unnecessary check before ↵Markus Elfring
drm_gem_object_unreference_unlocked() The drm_gem_object_unreference_unlocked() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
2016-07-19drm/rockchip: fix a couple off by one bugsDan Carpenter
The priv->crtc_funcs[] array has ROCKCHIP_MAX_CRTC elements so > should be >= here. Fixes: 2048e3286f34 ('drm: rockchip: Add basic drm driver') Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> Reviewed-by: Sean Paul <seanpaul@chromium.org>
2016-07-19drm/rockchip: vop: correct rk3036 register defineMark Yao
Signed-off-by: Mark Yao <mark.yao@rock-chips.com> Reported-by: Tomasz Figa <tfiga@chromium.org>
2016-07-19drm/rockchip: vop: correct the source size of uv scale factor settingMark Yao
When the input color format is YUV, we need to do some external scale for CBCR. Like, * In YUV420 data format: cbcr_xscale = dst_w / src_w * 2; cbcr_yscale = dst_h / src_h * 2; * In YUV422 data format: cbcr_xscale = dst_w / src_w * 2; cbcr_yscale = dst_h / src_h; * In YUV444 data format cbcr_xscale = dst_w / src_w; cbcr_yscale = dst_h / src_h; Signed-off-by: Yakir Yang <ykk@rock-chips.com>
2016-07-19drm/rockchip: vop: add uv_vir register field for RK3036 VOPYakir Yang
The WIN0 of RK3036 VOP could support YUV data format, but driver forget to add the uv_vir register field for it. Signed-off-by: Yakir Yang <ykk@rock-chips.com>
2016-07-19drm/rockchip: fix "should it be static?" warningsJohn Keeping
Combined with the previous commit, this fixes all of the sparse warnings in drm/rockchip. Signed-off-by: John Keeping <john@metanate.com>
2016-07-19drm/rockchip: fb: add missing headerJohn Keeping
This fixes the following sparse warnings: drivers/gpu/drm/rockchip/rockchip_drm_fb.c:32:23: warning: symbol 'rockchip_fb_get_gem_obj' was not declared. Should it be static? drivers/gpu/drm/rockchip/rockchip_drm_fb.c:315:24: warning: symbol 'rockchip_drm_framebuffer_init' was not declared. Should it be static? drivers/gpu/drm/rockchip/rockchip_drm_fb.c:329:6: warning: symbol 'rockchip_drm_mode_config_init' was not declared. Should it be static? Signed-off-by: John Keeping <john@metanate.com>
2016-07-19drm/rockchip: dw_hdmi: remove unused #includeJohn Keeping
drm_encoder_slave is not used in this file. Signed-off-by: John Keeping <john@metanate.com>
2016-07-18drm: rcar-du: Remove i2c slave encoder interface for hdmi encoderArchit Taneja
The hdmi output in rcar-du uses the i2c slave encoder interface to link to the adv7511 encoder chip. The kms driver creates encoder and connector entities that internally uses the drm_encoder_slave_funcs ops provided by the slave encoder driver. Change the driver such that it expects a bridge entity instead of a slave encoder. The hdmi connector code isn't needed anymore as we expect the adv7511 bridge driver to create/manage the connector. Note that the kms driver still expects a connector node for hdmi to be present in DT. This node has no connection to the connector created by the bridge driver. Tested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Archit Taneja <architt@codeaurora.org>
2016-07-17drm/bridge: tc358767: Add DPI to eDP bridge driverAndrey Gusakov
Add a drm_bridge driver for the Toshiba TC358767 DPI/DSI to eDP/DP bridge. Currently only DPI input with 24-bit RGB is supported. Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> Signed-off-by: Archit Taneja <architt@codeaurora.org>
2016-07-16drm/msm: Delete an unnecessary check before drm_gem_object_unreference()Markus Elfring
The drm_gem_object_unreference() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring <elfring@users.sourceforge.net> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: Delete unnecessary checks before drm_gem_object_unreference_unlocked()Markus Elfring
The drm_gem_object_unreference_unlocked() function tests whether its argument is NULL and then returns immediately. Thus the test around the calls is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring <elfring@users.sourceforge.net> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/hdmi: Delete an unnecessary check before the function call "kfree"Markus Elfring
The kfree() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring <elfring@users.sourceforge.net> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: return -EFAULT instead of bytes remainingDan Carpenter
copy_to/from_user returns the number of bytes remaining to be copied but we want to return -EFAULT. Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/hdmi: use PTR_ERR_OR_ZERO() to simplify the codeWei Yongjun
Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR. Generated by: scripts/coccinelle/api/ptr_ret.cocci Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: add missing of_node_put after calling of_parse_phandlePeter Chen
of_node_put needs to be called when the device node which is got from of_parse_phandle has finished using. Signed-off-by: Peter Chen <peter.chen@nxp.com> [rebased on top of Archit's DT rework, so looses one hunk] Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: Replace drm_fb_get_bpp_depth() with drm_format_plane_cpp()Laurent Pinchart
The driver needs the number of bytes per pixel, not the bpp and depth info meant for fbdev compatibility. Use the right API. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/dsi: Fix return value check in msm_dsi_host_set_display_mode()Wei Yongjun
In case of error, the function drm_mode_duplicate() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn> Reviewed-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm: msm: Add ASoC generic hdmi audio codec support.Srinivas Kandagatla
This patch adds support to generic audio codec via ASoC hdmi-codec infrastucture which is merged recently. Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> [rebased on efc9194] Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/rd: add module param to dump all bo'sRob Clark
By default, if using $debugfs/.../rd to log cmdstream, only the cmdstream buffers themselves are logged. But in some cases we want to capture other buffers in the submit (to see VBO's or shaders). So add a mod-param knob to control this. Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/rd: split out snapshot_buf helperRob Clark
(reduce the noise in next patch) Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: bump kernel api versionRob Clark
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: deal with arbitrary # of cmd buffersRob Clark
For some optimizations coming on the userspace side, splitting larger draw or gmem cmds into multiple cmdstream buffers, we need to support much more than the previous small/arbitrary limit. Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: wire up vmap shrinkerRob Clark
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: change gem->vmap() to get/putRob Clark
Before we can add vmap shrinking, we really need to know which vmap'ings are currently being used. So switch to get/put interface. Stubbed put fxns for now. Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: shrinker supportRob Clark
For a first step, only purge obj->madv==DONTNEED objects. We could be more agressive and next try unpinning inactive objects.. but that is only useful if you have swap. Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: add put_iova() helperRob Clark
We'll need this too for shrinker/purging. Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: add madvise ioctlRob Clark
Doesn't do anything too interesting until we wire up shrinker. Pretty much lifted from i915. Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: use mutex_lock_interruptible for submit ioctlRob Clark
Be kinder to things that do lots of signal handling (ie. Xorg) Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/dsi: Don't get DSI index from DTArchit Taneja
The DSI host and PHY driver currently expects the DT bindings to provide custom properties "qcom,dsi-host-index" and "qcom,dsi-phy-index" so that the driver can identify which DSI instance it is. The binding isn't acceptable, but the driver still needs to figure out what its instance id. This is now done by storing the mmio starting addresses for each DSI instance in every SoC version in the driver. The driver then identifies the index number by trying to match the stored address with comparing the resource start address we get from DT. We don't have compatible strings for DSI PHY on each SoC, but only the DSI PHY type. We only support one SoC version for each PHY type, so we get away doing the same thing above for the PHY driver. We can revisit this when we support two SoCs with the same DSI PHY. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/mdp5: Update compatible strings for MDSS/MDP5Archit Taneja
Introduce new compatible strings for the top level MDSS wrapper device, and the MDP5 device. Previously, the "qcom,mdp5" and "qcom,mdss_mdp" compatible strings were used to match the top level platform_device (which was also tied to the top level drm_device struct). Now, these strings are used to match the MDP5 platform device. Use "qcom,mdss" as the compatible string for top level MDSS device. This is now used to match the top level platform_device (which is tied to the drm_device struct). Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: Drop the gpu bindingArchit Taneja
The driver currently identifies the GPU components it needs by parsing a phandle list from the 'gpus' DT property. This isn't the right binding to go with. So, for now, just search all device nodes and find the gpu node we need by parsing a list of compatible strings. Once we know how to link the kms and gpu drivers, we'll drop this method and use the correct binding. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: Add components for MDP5Archit Taneja
For MDP5 based platforms, the master device isn't the MDP5 platform device, but the top level MDSS device, which is a parent to MDP5 and interface (DSI, HDMI, eDP etc) devices. In order to add components on MDP5 platforms, we first need to populate the MDSS children, locate the MDP5 child, and then parse its ports to get the display interfaces. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: Add display components by parsing MDP portsArchit Taneja
The kms driver currently identifies all the mdss components it needs by parsing a phandle list from the 'connectors' DT property. Instead of this, describe a list of ports that the MDP hardware provides to the external world. These ports are linked to external encoder interfaces such as DSI, HDMI. These are also the subcomponent devices that we need add. This description of ports complies with the generic graph bindings. The LVDS port is a special case since it is a part of MDP4 itself, and its output connects directly to the LVDS panel. In this case, we don't try to add it as a component. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: Create separate funcs for adding display/gpu componentsArchit Taneja
Simplifies some of the code that we'll add later. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/mdp5: Add missing mdp5_enable/disable callsArchit Taneja
Since runtime PM isn't implemented yet, we need to call mdp5_enable/disable in a few more places. These would later be replaced by runtime PM get/put calls. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm: Call pm_runtime_enable/disable for newly created devicesArchit Taneja
With the new device hierarchy for MDP5, we need to enable runtime PM for both the toplevel MDSS device and the MDP5 device itself. Enable runtime PM for the new devices. Since MDP4 and MDP5 now have different places where runtime PM is enabled, remove the previous pm_runtime_enable/disable calls, and squash them in the respective kms drivers. The new device hierarchy (as expressed in the DT bindings) has the GDSC tied only to the MDSS wrapper device. This GDSC needs to be enabled for accessing any register in the MDSS sub-blocks. Once every driver is runtime adapted, the GDSC will be enabled when any sub-block device calls runtime_get because of the parent-child relationship with MDSS. Until then, we call pm_runtime_get_sync() once for the MDSS device to ensure the GDSC is never disabled. This will be removed once all the drivers are runtime PM adapted. The error handling paths become a bit tricky when we call these runtime PM funcs. There doesn't seem to be any helper that checks if runtime PM is enabled already. Add bool variables in mdp4_kms/mdp5_kms structs to check if the driver had managed to call pm_runtime_enable before bailing out. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/mdp5: Update the register offsets of MDP5 sub-blocksArchit Taneja
The MDP5 sub-block register offsets are relative to the top level MDSS register address. Now that we have the start of MDP5 register address space, provide the offsets relative to that. This involves subtracting the offsets with 0x1000 or 0x100 depending on the MDP5 version. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/mdp5: Use updated MDP5 register namesArchit Taneja
Since MDSS registers were stuffed within the the MDP5 register space, we had an __offset_MDP() macro to identify the offset between the start of MDSS and MDP5 address spaces. This offset macro expected a MDP index argument, which didn't make much sense since we don't have multiple MDPs. The offset is no longer needed now that we have devices for the 2 different register address spaces. Also, remove the "REG_MDP5_MDP_" prefix to "REG_MDP5_". Update the generated headers in mdp5.xml.h We generally update headers as a separate patch, but we need to do these together to prevent breaking build. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/mdp5: Remove old kms init/destroy funcsArchit Taneja
With the new kms_init/destroy funcs in place for MDP5, we can get rid of the old kms funcs. Some members of the mdp5_kms struct also become redundant, so we remove those too. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/mdp5: Use the new hierarchy and drop old irq managementArchit Taneja
Call msm_mdss_init in msm_drv to set up top level registers/irq line. Start using the new kms_init2/destroy2 funcs to inititalize MDP5 KMS. With the MDSS interrupt and irqdomain set up, the old MDP5 irq code can be dropped. The mdp5_hw_init kms func now uses the platform device tied to MDP5 instead of the one tied to the drm_device/MDSS. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/mdp5: Prepare new kms_init funcsArchit Taneja
With MDP5 as a new device, we need to do less for MDP when initializing modeset after all the components are bound. Create mdp5_kms_init2/destroy2 funcs that inits modeset. These will eventually replace the older kms_init/destroy funcs. In the new kms_init2, the platform_device used is the one corresponding to the new MDP5 platform_device. The new change here is that the irq is now retrieved using irq_of_parse_and_map(), since MDP5 is a child interrupt of the MDSS interrupt controller. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/mdp5: Create a separate MDP5 deviceArchit Taneja
In order to have a tree-like device hierarchy between MDSS and its sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate device/driver for MDP5. Currently, MDP5 and MDSS are squashed together are are tied to the top level platform_device, which is also the one used to create drm_device. The mdp5_kms_init code is split into two parts. The part where device resources are allocated are associated with the MDP5 driver's probe, the rest is executed later when we initialize modeset. With this change, unlike MDP4, the MDP5 platform_device isn't tied to the top level drm_device anymore. The top level drm_device is now associated with a platform device that corresponds to MDSS wrapper hardware. Create mdp5_init/destroy funcs that will be used by the MDP5 driver probe/remove. Use the HW_VERSION register in the MDP5 register address space. Both the MDSS and MDP VERSION registers give out identical version info. The older mdp5_kms_init code is left as is for now, this would be removed later when we have all the pieces to support the new device hierarchy. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-07-16drm/msm/mdp5: Add MDSS top level driverArchit Taneja
SoCs that contain MDP5 have a top level wrapper called MDSS that manages clocks, power and irq for the sub-blocks within it. Currently, the MDSS portions are stuffed into the MDP5 driver. This makes it hard to represent the DT bindings in the correct way. We create a top level MDSS helper that handles these parts. This is essentially moving out some of the mdp5_kms irq code and MDSS register space and keeping it as a separate entity. We haven't given any clocks to the top level MDSS yet, but a AHB clock would be added in the future to access registers. One thing to note is that the resources allocated by this helper are tied to the top level platform_device (the one that allocates the drm_device struct too). This device would be the parent to MDSS sub-blocks like MDP5, DSI, eDP etc. Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>